diff --git a/usr.bin/apply/apply.c b/usr.bin/apply/apply.c index edeb9c60679a..727351c8ff87 100644 --- a/usr.bin/apply/apply.c +++ b/usr.bin/apply/apply.c @@ -1,251 +1,247 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include #include #define ISMAGICNO(p) \ (p)[0] == magic && isdigit((unsigned char)(p)[1]) && (p)[1] != '0' static int exec_shell(const char *, const char *, const char *); static void usage(void); int main(int argc, char *argv[]) { struct sbuf *cmdbuf; long arg_max; int ch, debug, i, magic, n, nargs, rval; size_t cmdsize; char buf[4]; char *cmd, *name, *p, *shell, *slashp, *tmpshell; debug = 0; magic = '%'; /* Default magic char is `%'. */ nargs = -1; while ((ch = getopt(argc, argv, "a:d0123456789")) != -1) switch (ch) { case 'a': if (optarg[0] == '\0' || optarg[1] != '\0') errx(1, "illegal magic character specification"); magic = optarg[0]; break; case 'd': debug = 1; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (nargs != -1) errx(1, "only one -# argument may be specified"); nargs = optopt - '0'; break; default: usage(); } argc -= optind; argv += optind; if (argc < 2) usage(); /* * The command to run is argv[0], and the args are argv[1..]. * Look for %digit references in the command, remembering the * largest one. */ for (n = 0, p = argv[0]; *p != '\0'; ++p) if (ISMAGICNO(p)) { ++p; if (p[0] - '0' > n) n = p[0] - '0'; } /* * Figure out the shell and name arguments to pass to execl() * in exec_shell(). Always malloc() shell and just set name * to point at the last part of shell if there are any backslashes, * otherwise just set it to point at the space malloc()'d. If * SHELL environment variable exists, replace contents of * shell with it. */ shell = name = NULL; tmpshell = getenv("SHELL"); shell = (tmpshell != NULL) ? strdup(tmpshell) : strdup(_PATH_BSHELL); if (shell == NULL) err(1, "strdup() failed"); slashp = strrchr(shell, '/'); name = (slashp != NULL) ? slashp + 1 : shell; /* * If there were any %digit references, then use those, otherwise * build a new command string with sufficient %digit references at * the end to consume (nargs) arguments each time round the loop. * Allocate enough space to hold the maximum command. Save the * size to pass to snprintf(). */ if (n == 0) { cmdsize = strlen(argv[0]) + 9 * (sizeof(" %1") - 1) + 1; if ((cmd = malloc(cmdsize)) == NULL) err(1, NULL); strlcpy(cmd, argv[0], cmdsize); /* If nargs not set, default to a single argument. */ if (nargs == -1) nargs = 1; for (i = 1; i <= nargs; i++) { snprintf(buf, sizeof(buf), " %c%d", magic, i); strlcat(cmd, buf, cmdsize); } /* * If nargs set to the special value 0, eat a single * argument for each command execution. */ if (nargs == 0) nargs = 1; } else { if ((cmd = strdup(argv[0])) == NULL) err(1, NULL); nargs = n; } cmdbuf = sbuf_new(NULL, NULL, 1024, SBUF_AUTOEXTEND); if (cmdbuf == NULL) err(1, NULL); arg_max = sysconf(_SC_ARG_MAX); /* * (argc) and (argv) are still offset by one to make it simpler to * expand %digit references. At the end of the loop check for (argc) * equals 1 means that all the (argv) has been consumed. */ for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) { sbuf_clear(cmdbuf); if (sbuf_cat(cmdbuf, "exec ") != 0) err(1, "sbuf"); /* Expand command argv references. */ for (p = cmd; *p != '\0'; ++p) { if (ISMAGICNO(p)) { if (sbuf_cat(cmdbuf, argv[*++p - '0']) != 0) err(1, "sbuf"); } else { if (sbuf_putc(cmdbuf, *p) != 0) err(1, "sbuf"); } if (sbuf_len(cmdbuf) > arg_max) errc(1, E2BIG, NULL); } /* Terminate the command string. */ if (sbuf_finish(cmdbuf) != 0) err(1, "sbuf"); /* Run the command. */ if (debug) (void)printf("%s\n", sbuf_data(cmdbuf)); else if (exec_shell(sbuf_data(cmdbuf), shell, name)) rval = 1; } if (argc != 1) errx(1, "expecting additional argument%s after \"%s\"", (nargs - argc) ? "s" : "", argv[argc - 1]); free(cmd); sbuf_delete(cmdbuf); free(shell); exit(rval); } /* * exec_shell -- * Execute a shell command using passed use_shell and use_name * arguments. */ static int exec_shell(const char *command, const char *use_shell, const char *use_name) { pid_t pid; int omask, pstat; sig_t intsave, quitsave; if (!command) /* just checking... */ return(1); omask = sigblock(sigmask(SIGCHLD)); switch(pid = vfork()) { case -1: /* error */ err(1, "vfork"); case 0: /* child */ (void)sigsetmask(omask); execl(use_shell, use_name, "-c", command, (char *)NULL); warn("%s", use_shell); _exit(1); } intsave = signal(SIGINT, SIG_IGN); quitsave = signal(SIGQUIT, SIG_IGN); pid = waitpid(pid, &pstat, 0); (void)sigsetmask(omask); (void)signal(SIGINT, intsave); (void)signal(SIGQUIT, quitsave); return(pid == -1 ? -1 : pstat); } static void usage(void) { (void)fprintf(stderr, "usage: apply [-a magic] [-d] [-0123456789] command arguments ...\n"); exit(1); } diff --git a/usr.bin/bc/bc.y b/usr.bin/bc/bc.y index 245748528a53..c270330281a8 100644 --- a/usr.bin/bc/bc.y +++ b/usr.bin/bc/bc.y @@ -1,1215 +1,1214 @@ %{ /* $OpenBSD: bc.y,v 1.46 2014/10/14 15:35:18 deraadt Exp $ */ /* * Copyright (c) 2003, Otto Moerbeek * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * This implementation of bc(1) uses concepts from the original 4.4 * BSD bc(1). The code itself is a complete rewrite, based on the * Posix defined bc(1) grammar. Other differences include type safe * usage of pointers to build the tree of emitted code, typed yacc * rule values, dynamic allocation of all data structures and a * completely rewritten lexical analyzer using lex(1). * * Some effort has been made to make sure that the generated code is * the same as the code generated by the older version, to provide * easy regression testing. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" #include "pathnames.h" #define BC_VER "1.1-FreeBSD" #define END_NODE ((ssize_t) -1) #define CONST_STRING ((ssize_t) -2) #define ALLOC_STRING ((ssize_t) -3) extern char *yytext; extern FILE *yyin; struct tree { union { char *astr; const char *cstr; } u; ssize_t index; }; int yywrap(void); int fileindex; int sargc; const char **sargv; const char *filename; char *cmdexpr; static void grow(void); static ssize_t cs(const char *); static ssize_t as(const char *); static ssize_t node(ssize_t, ...); static void emit(ssize_t, int); static void emit_macro(int, ssize_t); static void free_tree(void); static ssize_t numnode(int); static ssize_t lookup(char *, size_t, char); static ssize_t letter_node(char *); static ssize_t array_node(char *); static ssize_t function_node(char *); static void add_par(ssize_t); static void add_local(ssize_t); static void warning(const char *); static void init(void); static void usage(void); static char *escape(const char *); static ssize_t instr_sz = 0; static struct tree *instructions = NULL; static ssize_t current = 0; static int macro_char = '0'; static int reset_macro_char = '0'; static int nesting = 0; static int breakstack[16]; static int breaksp = 0; static ssize_t prologue; static ssize_t epilogue; static bool st_has_continue; static char str_table[UCHAR_MAX][2]; static bool do_fork = true; static u_short var_count; static pid_t dc; static void sigchld(int); extern char *__progname; #define BREAKSTACK_SZ (sizeof(breakstack)/sizeof(breakstack[0])) /* These values are 4.4BSD bc compatible */ #define FUNC_CHAR 0x01 #define ARRAY_CHAR 0xa1 /* Skip '\0', [, \ and ] */ #define ENCODE(c) ((c) < '[' ? (c) : (c) + 3); #define VAR_BASE (256-4) #define MAX_VARIABLES (VAR_BASE * VAR_BASE) const struct option long_options[] = { {"expression", required_argument, NULL, 'e'}, {"help", no_argument, NULL, 'h'}, {"mathlib", no_argument, NULL, 'l'}, /* compatibility option */ {"quiet", no_argument, NULL, 'q'}, {"version", no_argument, NULL, 'v'}, {NULL, no_argument, NULL, 0} }; %} %start program %union { struct lvalue lvalue; const char *str; char *astr; ssize_t node; } %token COMMA SEMICOLON LPAR RPAR LBRACE RBRACE LBRACKET RBRACKET DOT %token NEWLINE %token LETTER %token NUMBER STRING %token DEFINE BREAK QUIT LENGTH %token RETURN FOR IF WHILE SQRT %token SCALE IBASE OBASE AUTO %token CONTINUE ELSE PRINT %left BOOL_OR %left BOOL_AND %nonassoc BOOL_NOT %nonassoc EQUALS LESS_EQ GREATER_EQ UNEQUALS LESS GREATER %right ASSIGN_OP %left PLUS MINUS %left MULTIPLY DIVIDE REMAINDER %right EXPONENT %nonassoc UMINUS %nonassoc INCR DECR %type named_expression %type argument_list %type alloc_macro %type expression %type function %type function_header %type input_item %type opt_argument_list %type opt_expression %type opt_relational_expression %type opt_statement %type print_expression %type print_expression_list %type relational_expression %type return_expression %type semicolon_list %type statement %type statement_list %% program : /* empty */ | program input_item ; input_item : semicolon_list NEWLINE { emit($1, 0); macro_char = reset_macro_char; putchar('\n'); free_tree(); st_has_continue = false; } | function { putchar('\n'); free_tree(); st_has_continue = false; } | error NEWLINE { yyerrok; } | error QUIT { yyerrok; } ; semicolon_list : /* empty */ { $$ = cs(""); } | statement | semicolon_list SEMICOLON statement { $$ = node($1, $3, END_NODE); } | semicolon_list SEMICOLON ; statement_list : /* empty */ { $$ = cs(""); } | statement | statement_list NEWLINE | statement_list NEWLINE statement { $$ = node($1, $3, END_NODE); } | statement_list SEMICOLON | statement_list SEMICOLON statement { $$ = node($1, $3, END_NODE); } ; opt_statement : /* empty */ { $$ = cs(""); } | statement ; statement : expression { $$ = node($1, cs("ps."), END_NODE); } | named_expression ASSIGN_OP expression { if ($2[0] == '\0') $$ = node($3, cs($2), $1.store, END_NODE); else $$ = node($1.load, $3, cs($2), $1.store, END_NODE); } | STRING { $$ = node(cs("["), as($1), cs("]P"), END_NODE); } | BREAK { if (breaksp == 0) { warning("break not in for or while"); YYERROR; } else { $$ = node( numnode(nesting - breakstack[breaksp-1]), cs("Q"), END_NODE); } } | CONTINUE { if (breaksp == 0) { warning("continue not in for or while"); YYERROR; } else { st_has_continue = true; $$ = node(numnode(nesting - breakstack[breaksp-1] - 1), cs("J"), END_NODE); } } | QUIT { sigset_t mask; putchar('q'); fflush(stdout); if (dc) { sigprocmask(SIG_BLOCK, NULL, &mask); sigsuspend(&mask); } else exit(0); } | RETURN return_expression { if (nesting == 0) { warning("return must be in a function"); YYERROR; } $$ = $2; } | FOR LPAR alloc_macro opt_expression SEMICOLON opt_relational_expression SEMICOLON opt_expression RPAR opt_statement pop_nesting { ssize_t n; if (st_has_continue) n = node($10, cs("M"), $8, cs("s."), $6, $3, END_NODE); else n = node($10, $8, cs("s."), $6, $3, END_NODE); emit_macro($3, n); $$ = node($4, cs("s."), $6, $3, cs(" "), END_NODE); } | IF LPAR alloc_macro pop_nesting relational_expression RPAR opt_statement { emit_macro($3, $7); $$ = node($5, $3, cs(" "), END_NODE); } | IF LPAR alloc_macro pop_nesting relational_expression RPAR opt_statement ELSE alloc_macro pop_nesting opt_statement { emit_macro($3, $7); emit_macro($9, $11); $$ = node($5, $3, cs("e"), $9, cs(" "), END_NODE); } | WHILE LPAR alloc_macro relational_expression RPAR opt_statement pop_nesting { ssize_t n; if (st_has_continue) n = node($6, cs("M"), $4, $3, END_NODE); else n = node($6, $4, $3, END_NODE); emit_macro($3, n); $$ = node($4, $3, cs(" "), END_NODE); } | LBRACE statement_list RBRACE { $$ = $2; } | PRINT print_expression_list { $$ = $2; } ; alloc_macro : /* empty */ { $$ = cs(str_table[macro_char]); macro_char++; /* Do not use [, \ and ] */ if (macro_char == '[') macro_char += 3; /* skip letters */ else if (macro_char == 'a') macro_char = '{'; else if (macro_char == ARRAY_CHAR) macro_char += 26; else if (macro_char == 255) fatal("program too big"); if (breaksp == BREAKSTACK_SZ) fatal("nesting too deep"); breakstack[breaksp++] = nesting++; } ; pop_nesting : /* empty */ { breaksp--; } ; function : function_header opt_parameter_list RPAR opt_newline LBRACE NEWLINE opt_auto_define_list statement_list RBRACE { int n = node(prologue, $8, epilogue, cs("0"), numnode(nesting), cs("Q"), END_NODE); emit_macro($1, n); reset_macro_char = macro_char; nesting = 0; breaksp = 0; } ; function_header : DEFINE LETTER LPAR { $$ = function_node($2); free($2); prologue = cs(""); epilogue = cs(""); nesting = 1; breaksp = 0; breakstack[breaksp] = 0; } ; opt_newline : /* empty */ | NEWLINE ; opt_parameter_list : /* empty */ | parameter_list ; parameter_list : LETTER { add_par(letter_node($1)); free($1); } | LETTER LBRACKET RBRACKET { add_par(array_node($1)); free($1); } | parameter_list COMMA LETTER { add_par(letter_node($3)); free($3); } | parameter_list COMMA LETTER LBRACKET RBRACKET { add_par(array_node($3)); free($3); } ; opt_auto_define_list : /* empty */ | AUTO define_list NEWLINE | AUTO define_list SEMICOLON ; define_list : LETTER { add_local(letter_node($1)); free($1); } | LETTER LBRACKET RBRACKET { add_local(array_node($1)); free($1); } | define_list COMMA LETTER { add_local(letter_node($3)); free($3); } | define_list COMMA LETTER LBRACKET RBRACKET { add_local(array_node($3)); free($3); } ; opt_argument_list : /* empty */ { $$ = cs(""); } | argument_list ; argument_list : expression | argument_list COMMA expression { $$ = node($1, $3, END_NODE); } | argument_list COMMA LETTER LBRACKET RBRACKET { $$ = node($1, cs("l"), array_node($3), END_NODE); free($3); } ; opt_relational_expression : /* empty */ { $$ = cs(" 0 0="); } | relational_expression ; relational_expression : expression EQUALS expression { $$ = node($1, $3, cs("="), END_NODE); } | expression UNEQUALS expression { $$ = node($1, $3, cs("!="), END_NODE); } | expression LESS expression { $$ = node($1, $3, cs(">"), END_NODE); } | expression LESS_EQ expression { $$ = node($1, $3, cs("!<"), END_NODE); } | expression GREATER expression { $$ = node($1, $3, cs("<"), END_NODE); } | expression GREATER_EQ expression { $$ = node($1, $3, cs("!>"), END_NODE); } | expression { $$ = node($1, cs(" 0!="), END_NODE); } ; return_expression : /* empty */ { $$ = node(cs("0"), epilogue, numnode(nesting), cs("Q"), END_NODE); } | expression { $$ = node($1, epilogue, numnode(nesting), cs("Q"), END_NODE); } | LPAR RPAR { $$ = node(cs("0"), epilogue, numnode(nesting), cs("Q"), END_NODE); } ; opt_expression : /* empty */ { $$ = cs(" 0"); } | expression ; expression : named_expression { $$ = node($1.load, END_NODE); } | DOT { $$ = node(cs("l."), END_NODE); } | NUMBER { $$ = node(cs(" "), as($1), END_NODE); } | LPAR expression RPAR { $$ = $2; } | LETTER LPAR opt_argument_list RPAR { $$ = node($3, cs("l"), function_node($1), cs("x"), END_NODE); free($1); } | MINUS expression %prec UMINUS { $$ = node(cs(" 0"), $2, cs("-"), END_NODE); } | expression PLUS expression { $$ = node($1, $3, cs("+"), END_NODE); } | expression MINUS expression { $$ = node($1, $3, cs("-"), END_NODE); } | expression MULTIPLY expression { $$ = node($1, $3, cs("*"), END_NODE); } | expression DIVIDE expression { $$ = node($1, $3, cs("/"), END_NODE); } | expression REMAINDER expression { $$ = node($1, $3, cs("%"), END_NODE); } | expression EXPONENT expression { $$ = node($1, $3, cs("^"), END_NODE); } | INCR named_expression { $$ = node($2.load, cs("1+d"), $2.store, END_NODE); } | DECR named_expression { $$ = node($2.load, cs("1-d"), $2.store, END_NODE); } | named_expression INCR { $$ = node($1.load, cs("d1+"), $1.store, END_NODE); } | named_expression DECR { $$ = node($1.load, cs("d1-"), $1.store, END_NODE); } | named_expression ASSIGN_OP expression { if ($2[0] == '\0') $$ = node($3, cs($2), cs("d"), $1.store, END_NODE); else $$ = node($1.load, $3, cs($2), cs("d"), $1.store, END_NODE); } | LENGTH LPAR expression RPAR { $$ = node($3, cs("Z"), END_NODE); } | SQRT LPAR expression RPAR { $$ = node($3, cs("v"), END_NODE); } | SCALE LPAR expression RPAR { $$ = node($3, cs("X"), END_NODE); } | BOOL_NOT expression { $$ = node($2, cs("N"), END_NODE); } | expression BOOL_AND alloc_macro pop_nesting expression { ssize_t n = node(cs("R"), $5, END_NODE); emit_macro($3, n); $$ = node($1, cs("d0!="), $3, END_NODE); } | expression BOOL_OR alloc_macro pop_nesting expression { ssize_t n = node(cs("R"), $5, END_NODE); emit_macro($3, n); $$ = node($1, cs("d0="), $3, END_NODE); } | expression EQUALS expression { $$ = node($1, $3, cs("G"), END_NODE); } | expression UNEQUALS expression { $$ = node($1, $3, cs("GN"), END_NODE); } | expression LESS expression { $$ = node($3, $1, cs("("), END_NODE); } | expression LESS_EQ expression { $$ = node($3, $1, cs("{"), END_NODE); } | expression GREATER expression { $$ = node($1, $3, cs("("), END_NODE); } | expression GREATER_EQ expression { $$ = node($1, $3, cs("{"), END_NODE); } ; named_expression : LETTER { $$.load = node(cs("l"), letter_node($1), END_NODE); $$.store = node(cs("s"), letter_node($1), END_NODE); free($1); } | LETTER LBRACKET expression RBRACKET { $$.load = node($3, cs(";"), array_node($1), END_NODE); $$.store = node($3, cs(":"), array_node($1), END_NODE); free($1); } | SCALE { $$.load = cs("K"); $$.store = cs("k"); } | IBASE { $$.load = cs("I"); $$.store = cs("i"); } | OBASE { $$.load = cs("O"); $$.store = cs("o"); } ; print_expression_list : print_expression | print_expression_list COMMA print_expression { $$ = node($1, $3, END_NODE); } print_expression : expression { $$ = node($1, cs("ds.n"), END_NODE); } | STRING { char *p = escape($1); $$ = node(cs("["), as(p), cs("]n"), END_NODE); free(p); } %% static void grow(void) { struct tree *p; size_t newsize; if (current == instr_sz) { newsize = instr_sz * 2 + 1; p = reallocarray(instructions, newsize, sizeof(*p)); if (p == NULL) { free(instructions); err(1, NULL); } instructions = p; instr_sz = newsize; } } static ssize_t cs(const char *str) { grow(); instructions[current].index = CONST_STRING; instructions[current].u.cstr = str; return (current++); } static ssize_t as(const char *str) { grow(); instructions[current].index = ALLOC_STRING; instructions[current].u.astr = strdup(str); if (instructions[current].u.astr == NULL) err(1, NULL); return (current++); } static ssize_t node(ssize_t arg, ...) { va_list ap; ssize_t ret; va_start(ap, arg); ret = current; grow(); instructions[current++].index = arg; do { arg = va_arg(ap, ssize_t); grow(); instructions[current++].index = arg; } while (arg != END_NODE); va_end(ap); return (ret); } static void emit(ssize_t i, int level) { if (level > 1000) errx(1, "internal error: tree level > 1000"); if (instructions[i].index >= 0) { while (instructions[i].index != END_NODE && instructions[i].index != i) { emit(instructions[i].index, level + 1); i++; } } else if (instructions[i].index != END_NODE) fputs(instructions[i].u.cstr, stdout); } static void emit_macro(int nodeidx, ssize_t code) { putchar('['); emit(code, 0); printf("]s%s\n", instructions[nodeidx].u.cstr); nesting--; } static void free_tree(void) { ssize_t i; for (i = 0; i < current; i++) if (instructions[i].index == ALLOC_STRING) free(instructions[i].u.astr); current = 0; } static ssize_t numnode(int num) { const char *p; if (num < 10) p = str_table['0' + num]; else if (num < 16) p = str_table['A' - 10 + num]; else errx(1, "internal error: break num > 15"); return (node(cs(" "), cs(p), END_NODE)); } static ssize_t lookup(char * str, size_t len, char type) { ENTRY entry, *found; u_char *p; u_short num; /* The scanner allocated an extra byte already */ if (str[len-1] != type) { str[len] = type; str[len+1] = '\0'; } entry.key = str; found = hsearch(entry, FIND); if (found == NULL) { if (var_count == MAX_VARIABLES) errx(1, "too many variables"); p = malloc(4); if (p == NULL) err(1, NULL); num = var_count++; p[0] = 255; p[1] = ENCODE(num / VAR_BASE + 1); p[2] = ENCODE(num % VAR_BASE + 1); p[3] = '\0'; entry.data = (char *)p; entry.key = strdup(str); if (entry.key == NULL) err(1, NULL); found = hsearch(entry, ENTER); if (found == NULL) err(1, NULL); } return (cs(found->data)); } static ssize_t letter_node(char *str) { size_t len; len = strlen(str); if (len == 1 && str[0] != '_') return (cs(str_table[(int)str[0]])); else return (lookup(str, len, 'L')); } static ssize_t array_node(char *str) { size_t len; len = strlen(str); if (len == 1 && str[0] != '_') return (cs(str_table[(int)str[0] - 'a' + ARRAY_CHAR])); else return (lookup(str, len, 'A')); } static ssize_t function_node(char *str) { size_t len; len = strlen(str); if (len == 1 && str[0] != '_') return (cs(str_table[(int)str[0] - 'a' + FUNC_CHAR])); else return (lookup(str, len, 'F')); } static void add_par(ssize_t n) { prologue = node(cs("S"), n, prologue, END_NODE); epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE); } static void add_local(ssize_t n) { prologue = node(cs("0S"), n, prologue, END_NODE); epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE); } void yyerror(const char *s) { char *p, *str; int n; if (yyin != NULL && feof(yyin)) n = asprintf(&str, "%s: %s:%d: %s: unexpected EOF", __progname, filename, lineno, s); else if (yytext[0] == '\n') n = asprintf(&str, "%s: %s:%d: %s: newline unexpected", __progname, filename, lineno, s); else if (isspace((unsigned char)yytext[0]) || !isprint((unsigned char)yytext[0])) n = asprintf(&str, "%s: %s:%d: %s: ascii char 0x%02x unexpected", __progname, filename, lineno, s, yytext[0] & 0xff); else n = asprintf(&str, "%s: %s:%d: %s: %s unexpected", __progname, filename, lineno, s, yytext); if (n == -1) err(1, NULL); fputs("c[", stdout); for (p = str; *p != '\0'; p++) { if (*p == '[' || *p == ']' || *p =='\\') putchar('\\'); putchar(*p); } fputs("]ec\n", stdout); free(str); } void fatal(const char *s) { errx(1, "%s:%d: %s", filename, lineno, s); } static void warning(const char *s) { warnx("%s:%d: %s", filename, lineno, s); } static void init(void) { unsigned int i; for (i = 0; i < UCHAR_MAX; i++) { str_table[i][0] = i; str_table[i][1] = '\0'; } if (hcreate(1 << 16) == 0) err(1, NULL); } static void usage(void) { fprintf(stderr, "usage: %s [-chlv] [-e expression] [file ...]\n", __progname); exit(1); } static char * escape(const char *str) { char *p, *ret; ret = malloc(strlen(str) + 1); if (ret == NULL) err(1, NULL); p = ret; while (*str != '\0') { /* * We get _escaped_ strings here. Single backslashes are * already converted to double backslashes */ if (*str == '\\') { if (*++str == '\\') { switch (*++str) { case 'a': *p++ = '\a'; break; case 'b': *p++ = '\b'; break; case 'f': *p++ = '\f'; break; case 'n': *p++ = '\n'; break; case 'q': *p++ = '"'; break; case 'r': *p++ = '\r'; break; case 't': *p++ = '\t'; break; case '\\': *p++ = '\\'; break; } str++; } else { *p++ = '\\'; *p++ = *str++; } } else *p++ = *str++; } *p = '\0'; return (ret); } /* ARGSUSED */ static void sigchld(int signo __unused) { pid_t pid; int status, save_errno = errno; for (;;) { pid = waitpid(dc, &status, WCONTINUED | WNOHANG); if (pid == -1) { if (errno == EINTR) continue; _exit(0); } else if (pid == 0) break; if (WIFEXITED(status) || WIFSIGNALED(status)) _exit(0); else break; } errno = save_errno; } static const char * dummy_prompt(void) { return (""); } int main(int argc, char *argv[]) { char *q; int p[2]; int ch, i; init(); setvbuf(stdout, NULL, _IOLBF, 0); sargv = reallocarray(NULL, argc, sizeof(char *)); if (sargv == NULL) err(1, NULL); if ((cmdexpr = strdup("")) == NULL) err(1, NULL); /* The d debug option is 4.4 BSD bc(1) compatible */ while ((ch = getopt_long(argc, argv, "cde:hlqv", long_options, NULL)) != -1) { switch (ch) { case 'c': case 'd': do_fork = false; break; case 'e': q = cmdexpr; if (asprintf(&cmdexpr, "%s%s\n", cmdexpr, optarg) == -1) err(1, NULL); free(q); break; case 'h': usage(); break; case 'l': sargv[sargc++] = _PATH_LIBB; break; case 'q': /* compatibility option */ break; case 'v': fprintf(stderr, "%s (BSD bc) %s\n", __progname, BC_VER); exit(0); break; default: usage(); } } argc -= optind; argv += optind; interactive = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO); for (i = 0; i < argc; i++) sargv[sargc++] = argv[i]; if (do_fork) { if (pipe(p) == -1) err(1, "cannot create pipe"); dc = fork(); if (dc == -1) err(1, "cannot fork"); else if (dc != 0) { signal(SIGCHLD, sigchld); close(STDOUT_FILENO); dup(p[1]); close(p[0]); close(p[1]); } else { close(STDIN_FILENO); dup(p[0]); close(p[0]); close(p[1]); execl(_PATH_DC, "dc", "-x", (char *)NULL); err(1, "cannot find dc"); } } if (interactive) { gettty(&ttysaved); el = el_init("bc", stdin, stderr, stderr); hist = history_init(); history(hist, &he, H_SETSIZE, 100); el_set(el, EL_HIST, history, hist); el_set(el, EL_EDITOR, "emacs"); el_set(el, EL_SIGNAL, 1); el_set(el, EL_PROMPT, dummy_prompt); el_set(el, EL_ADDFN, "bc_eof", "", bc_eof); el_set(el, EL_BIND, "^D", "bc_eof", NULL); el_source(el, NULL); } yywrap(); return (yyparse()); } diff --git a/usr.bin/brandelf/brandelf.c b/usr.bin/brandelf/brandelf.c index 2c908efdc227..e99af06c428a 100644 --- a/usr.bin/brandelf/brandelf.c +++ b/usr.bin/brandelf/brandelf.c @@ -1,232 +1,231 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2000, 2001 David O'Brien * Copyright (c) 1996 Søren Schmidt * 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 * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int elftype(const char *); static const char *iselftype(int); static void printelftypes(void); static void usage(void) __dead2; struct ELFtypes { const char *str; int value; }; /* XXX - any more types? */ static struct ELFtypes elftypes[] = { { "FreeBSD", ELFOSABI_FREEBSD }, { "Linux", ELFOSABI_LINUX }, { "Solaris", ELFOSABI_SOLARIS }, { "SVR4", ELFOSABI_SYSV } }; int main(int argc, char **argv) { const char *strtype = "FreeBSD"; int ch, flags, retval, type; bool change, force, listed; fileargs_t *fa; cap_rights_t rights; type = ELFOSABI_FREEBSD; retval = 0; change = false; force = false; listed = false; while ((ch = getopt(argc, argv, "f:lt:v")) != -1) switch (ch) { case 'f': if (change) errx(1, "f option incompatible with t option"); force = true; type = atoi(optarg); if (errno == ERANGE || type < 0 || type > 255) { warnx("invalid argument to option f: %s", optarg); usage(); } break; case 'l': printelftypes(); listed = true; break; case 'v': /* does nothing */ break; case 't': if (force) errx(1, "t option incompatible with f option"); change = true; strtype = optarg; break; default: usage(); } argc -= optind; argv += optind; if (argc == 0) { if (listed) exit(0); else { warnx("no file(s) specified"); usage(); } } if (!force && (type = elftype(strtype)) == -1) { warnx("invalid ELF type '%s'", strtype); printelftypes(); usage(); } flags = change || force ? O_RDWR : O_RDONLY; cap_rights_init(&rights, CAP_READ, CAP_SEEK); if (flags == O_RDWR) cap_rights_set(&rights, CAP_WRITE); fa = fileargs_init(argc, argv, flags, 0, &rights, FA_OPEN); if (fa == NULL) err(1, "unable to init casper"); caph_cache_catpages(); if (caph_limit_stdio() < 0 || caph_enter_casper() < 0) err(1, "unable to enter capability mode"); while (argc != 0) { int fd; char buffer[EI_NIDENT]; if ((fd = fileargs_open(fa, argv[0])) < 0) { warn("error opening file %s", argv[0]); retval = 1; goto fail; } if (read(fd, buffer, EI_NIDENT) < EI_NIDENT) { warnx("file '%s' too short", argv[0]); retval = 1; goto fail; } if (buffer[0] != ELFMAG0 || buffer[1] != ELFMAG1 || buffer[2] != ELFMAG2 || buffer[3] != ELFMAG3) { warnx("file '%s' is not ELF format", argv[0]); retval = 1; goto fail; } if (!change && !force) { fprintf(stdout, "File '%s' is of brand '%s' (%u).\n", argv[0], iselftype(buffer[EI_OSABI]), buffer[EI_OSABI]); if (!iselftype(type)) { warnx("ELF ABI Brand '%u' is unknown", type); printelftypes(); } } else { buffer[EI_OSABI] = type; lseek(fd, 0, SEEK_SET); if (write(fd, buffer, EI_NIDENT) != EI_NIDENT) { warn("error writing %s %d", argv[0], fd); retval = 1; goto fail; } } fail: close(fd); argc--; argv++; } fileargs_free(fa); return (retval); } static void usage(void) { (void)fprintf(stderr, "usage: brandelf [-lv] [-f ELF_ABI_number] [-t string] file ...\n"); exit(1); } static const char * iselftype(int etype) { size_t elfwalk; for (elfwalk = 0; elfwalk < nitems(elftypes); elfwalk++) if (etype == elftypes[elfwalk].value) return (elftypes[elfwalk].str); return (0); } static int elftype(const char *elfstrtype) { size_t elfwalk; for (elfwalk = 0; elfwalk < nitems(elftypes); elfwalk++) if (strcasecmp(elfstrtype, elftypes[elfwalk].str) == 0) return (elftypes[elfwalk].value); return (-1); } static void printelftypes(void) { size_t elfwalk; fprintf(stderr, "known ELF types are: "); for (elfwalk = 0; elfwalk < nitems(elftypes); elfwalk++) fprintf(stderr, "%s(%u) ", elftypes[elfwalk].str, elftypes[elfwalk].value); fprintf(stderr, "\n"); } diff --git a/usr.bin/bsdiff/bsdiff/bsdiff.c b/usr.bin/bsdiff/bsdiff/bsdiff.c index 9836b8ee96f4..a4813253b23e 100644 --- a/usr.bin/bsdiff/bsdiff/bsdiff.c +++ b/usr.bin/bsdiff/bsdiff/bsdiff.c @@ -1,338 +1,337 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright 2003-2005 Colin Percival * All rights reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted providing that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #ifndef O_BINARY #define O_BINARY 0 #endif #include "divsufsort64.h" #define saidx_t saidx64_t #define divsufsort divsufsort64 #define MIN(x,y) (((x)<(y)) ? (x) : (y)) static off_t matchlen(u_char *old,off_t oldsize,u_char *new,off_t newsize) { off_t i; for(i=0;(iy) { *pos=I[st]; return x; } else { *pos=I[en]; return y; } } x=st+(en-st)/2; if(memcmp(old+I[x],new,MIN(oldsize-I[x],newsize))<0) { return search(I,old,oldsize,new,newsize,x,en,pos); } else { return search(I,old,oldsize,new,newsize,st,x,pos); }; } static void offtout(off_t x,u_char *buf) { off_t y; if(x<0) y=-x; else y=x; buf[0]=y%256;y-=buf[0]; y=y/256;buf[1]=y%256;y-=buf[1]; y=y/256;buf[2]=y%256;y-=buf[2]; y=y/256;buf[3]=y%256;y-=buf[3]; y=y/256;buf[4]=y%256;y-=buf[4]; y=y/256;buf[5]=y%256;y-=buf[5]; y=y/256;buf[6]=y%256;y-=buf[6]; y=y/256;buf[7]=y%256; if(x<0) buf[7]|=0x80; } static void usage(void) { fprintf(stderr, "usage: bsdiff oldfile newfile patchfile\n"); exit(1); } int main(int argc,char *argv[]) { int fd; u_char *old,*new; off_t oldsize,newsize; saidx_t *I; off_t scan,pos,len; off_t lastscan,lastpos,lastoffset; off_t oldscore,scsc; off_t s,Sf,lenf,Sb,lenb; off_t overlap,Ss,lens; off_t i; off_t dblen,eblen; u_char *db,*eb; u_char buf[8]; u_char header[32]; FILE * pf; BZFILE * pfbz2; int bz2err; if (argc != 4) usage(); /* Allocate oldsize+1 bytes instead of oldsize bytes to ensure that we never try to malloc(0) and get a NULL pointer */ if(((fd=open(argv[1],O_RDONLY|O_BINARY,0))<0) || ((oldsize=lseek(fd,0,SEEK_END))==-1)) err(1, "%s", argv[1]); if (oldsize > SSIZE_MAX || (uintmax_t)oldsize >= SIZE_T_MAX / sizeof(off_t) || oldsize == OFF_MAX) { errno = EFBIG; err(1, "%s", argv[1]); } if (((old=malloc(oldsize+1))==NULL) || (lseek(fd,0,SEEK_SET)!=0) || (read(fd,old,oldsize)!=oldsize) || (close(fd)==-1)) err(1,"%s",argv[1]); if(((I=malloc((oldsize+1)*sizeof(saidx_t)))==NULL)) err(1,NULL); if(divsufsort(old, I, oldsize)) err(1, "divsufsort"); /* Allocate newsize+1 bytes instead of newsize bytes to ensure that we never try to malloc(0) and get a NULL pointer */ if(((fd=open(argv[2],O_RDONLY|O_BINARY,0))<0) || ((newsize=lseek(fd,0,SEEK_END))==-1)) err(1, "%s", argv[2]); if (newsize > SSIZE_MAX || (uintmax_t)newsize >= SIZE_T_MAX || newsize == OFF_MAX) { errno = EFBIG; err(1, "%s", argv[2]); } if (((new=malloc(newsize+1))==NULL) || (lseek(fd,0,SEEK_SET)!=0) || (read(fd,new,newsize)!=newsize) || (close(fd)==-1)) err(1,"%s",argv[2]); if(((db=malloc(newsize+1))==NULL) || ((eb=malloc(newsize+1))==NULL)) err(1,NULL); dblen=0; eblen=0; /* Create the patch file */ if ((pf = fopen(argv[3], "wb")) == NULL) err(1, "%s", argv[3]); /* Header is 0 8 "BSDIFF40" 8 8 length of bzip2ed ctrl block 16 8 length of bzip2ed diff block 24 8 length of new file */ /* File is 0 32 Header 32 ?? Bzip2ed ctrl block ?? ?? Bzip2ed diff block ?? ?? Bzip2ed extra block */ memcpy(header,"BSDIFF40",8); offtout(0, header + 8); offtout(0, header + 16); offtout(newsize, header + 24); if (fwrite(header, 32, 1, pf) != 1) err(1, "fwrite(%s)", argv[3]); /* Compute the differences, writing ctrl as we go */ if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL) errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err); scan=0;len=0;pos=0; lastscan=0;lastpos=0;lastoffset=0; while(scanoldscore+8)) break; if((scan+lastoffsetSf*2-lenf) { Sf=s; lenf=i; } } lenb=0; if(scan=lastscan+i)&&(pos>=i);i++) { if(old[pos-i]==new[scan-i]) s++; if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; } } } if(lastscan+lenf>scan-lenb) { overlap=(lastscan+lenf)-(scan-lenb); s=0;Ss=0;lens=0; for(i=0;iSs) { Ss=s; lens=i+1; } } lenf+=lens-overlap; lenb-=lens; } for(i=0;i #include #include #include #include #include #include static char **args; static u_int cargs, nargs; static void addarg(const char *); static void addlib(const char *); static void usage(void) __dead2; int main(int argc, char *argv[]) { int ch, i; args = NULL; cargs = nargs = 0; while ((ch = getopt(argc, argv, "cD:EgI:L:o:O:sU:l:")) != -1) { if (ch == 'l') { /* Gone too far. Back up and get out. */ if (argv[optind - 1][0] == '-') optind -= 1; else optind -= 2; break; } else if (ch == '?') usage(); } addarg("/usr/bin/cc"); addarg("-std=iso9899:1999"); addarg("-pedantic"); for (i = 1; i < optind; i++) addarg(argv[i]); while (i < argc) { if (strncmp(argv[i], "-l", 2) == 0) { if (argv[i][2] != '\0') addlib(argv[i++] + 2); else { if (argv[++i] == NULL) usage(); addlib(argv[i++]); } } else addarg(argv[i++]); } execv("/usr/bin/cc", args); err(1, "/usr/bin/cc"); } static void addarg(const char *item) { if (nargs + 1 >= cargs) { cargs += 16; if ((args = realloc(args, sizeof(*args) * cargs)) == NULL) err(1, "malloc"); } if ((args[nargs++] = strdup(item)) == NULL) err(1, "strdup"); args[nargs] = NULL; } static void addlib(const char *lib) { if (strcmp(lib, "pthread") == 0) /* FreeBSD's gcc uses -pthread instead of -lpthread. */ addarg("-pthread"); else if (strcmp(lib, "rt") == 0) /* librt functionality is in libc or unimplemented. */ ; else if (strcmp(lib, "xnet") == 0) /* xnet functionality is in libc. */ ; else { addarg("-l"); addarg(lib); } } static void usage(void) { (void)fprintf(stderr, "%s\n%s\n", "usage: c99 [-cEgs] [-D name[=value]] ... [-I directory] ... [-L directory] ...", " [-o outfile] [-O optlevel] [-U name] ... operand ..."); exit(1); } diff --git a/usr.bin/chat/chat.c b/usr.bin/chat/chat.c index c15c3037d8a0..64dc6cba0dcf 100644 --- a/usr.bin/chat/chat.c +++ b/usr.bin/chat/chat.c @@ -1,1527 +1,1526 @@ /* * Chat -- a program for automatic session establishment (i.e. dial * the phone and log in). * * Standard termination codes: * 0 - successful completion of the script * 1 - invalid argument, expect string too large, etc. * 2 - error on an I/O operation or fatal error condition. * 3 - timeout waiting for a simple string. * 4 - the first string declared as "ABORT" * 5 - the second string declared as "ABORT" * 6 - ... and so on for successive ABORT strings. * * This software is in the public domain. * * ----------------- * added -T and -U option and \T and \U substitution to pass a phone * number into chat script. Two are needed for some ISDN TA applications. * Keith Dart * * * Added SAY keyword to send output to stderr. * This allows to turn ECHO OFF and to output specific, user selected, * text to give progress messages. This best works when stderr * exists (i.e.: pppd in nodetach mode). * * Added HANGUP directives to allow for us to be called * back. When HANGUP is set to NO, chat will not hangup at HUP signal. * We rely on timeouts in that case. * * Added CLR_ABORT to clear previously set ABORT string. This has been * dictated by the HANGUP above as "NO CARRIER" (for example) must be * an ABORT condition until we know the other host is going to close * the connection for call back. As soon as we have completed the * first stage of the call back sequence, "NO CARRIER" is a valid, non * fatal string. As soon as we got called back (probably get "CONNECT"), * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command. * Note that CLR_ABORT packs the abort_strings[] array so that we do not * have unused entries not being reclaimed. * * In the same vein as above, added CLR_REPORT keyword. * * Allow for comments. Line starting with '#' are comments and are * ignored. If a '#' is to be expected as the first character, the * expect string must be quoted. * * * Francis Demierre * Thu May 15 17:15:40 MET DST 1997 * * * Added -r "report file" switch & REPORT keyword. * Robert Geer * * Added -s "use stderr" and -S "don't use syslog" switches. * June 18, 1997 * Karl O. Pinc * * * Added -e "echo" switch & ECHO keyword * Dick Streefland * * * Considerable updates and modifications by * Al Longyear * Paul Mackerras * * * The original author is: * * Karl Fox * Morning Star Technologies, Inc. * 1760 Zollinger Road * Columbus, OH 43221 * (614)451-1883 * * */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define STR_LEN 1024 #ifndef SIGTYPE #define SIGTYPE void #endif #ifndef O_NONBLOCK #define O_NONBLOCK O_NDELAY #endif #define MAX_ABORTS 50 #define MAX_REPORTS 50 #define DEFAULT_CHAT_TIMEOUT 45 static int echo; static int verbose; static int to_log; static int to_stderr; static int Verbose; static int quiet; static int exit_code; static FILE* report_fp; static char *report_file; static char *chat_file; static char *phone_num; static char *phone_num2; static int timeout = DEFAULT_CHAT_TIMEOUT; static char blank[] = ""; static int have_tty_parameters; #define term_parms struct termios #define get_term_param(param) tcgetattr(0, param) #define set_term_param(param) tcsetattr(0, TCSANOW, param) static struct termios saved_tty_parameters; static char *abort_string[MAX_ABORTS], *fail_reason, fail_buffer[50]; static int n_aborts, abort_next, timeout_next, echo_next; static int clear_abort_next; static char *report_string[MAX_REPORTS]; static char report_buffer[50]; static int n_reports, report_next, report_gathering; static int clear_report_next; static int say_next, hup_next; void *dup_mem(void *b, size_t c); void *copy_of(char *s); static void usage(void) __dead2; void chat_logf(const char *fmt, ...); void fatal(int code, const char *fmt, ...); SIGTYPE sigalrm(int signo); SIGTYPE sigint(int signo); SIGTYPE sigterm(int signo); SIGTYPE sighup(int signo); void init(void); void set_tty_parameters(void); void echo_stderr(int); void break_sequence(void); void terminate(int status); void do_file(char *chatfile); int get_string(char *string); int put_string(char *s); int write_char(int c); int put_char(int c); int get_char(void); void chat_send(char *s); char *character(int c); void chat_expect(char *s); char *clean(char *s, int sending); void pack_array(char **array, int end); char *expect_strtok(char *, const char *); int vfmtmsg(char *, int, const char *, va_list); /* vsprintf++ */ void * dup_mem(void *b, size_t c) { void *ans = malloc (c); if (!ans) fatal(2, "memory error!"); memcpy (ans, b, c); return ans; } void * copy_of(char *s) { return dup_mem (s, strlen (s) + 1); } /* * chat [-esSvV] [-f chat-file] [-r report-file] [-t timeout] * [-T phone-number] [-U phone-number2] [chat-script] * where chat-script has the form: * [...[[expect[-send[-expect...]] send expect[-send[-expect]] ...]]] * * Perform a UUCP-dialer-like chat script on stdin and stdout. */ int main(int argc, char *argv[]) { int option; tzset(); while ((option = getopt(argc, argv, "ef:r:sSt:T:U:vV")) != -1) { switch (option) { case 'e': ++echo; break; case 'f': if (chat_file != NULL) free(chat_file); chat_file = copy_of(optarg); break; case 'r': if (report_fp != NULL) fclose(report_fp); if (report_file != NULL) free(report_file); report_file = copy_of(optarg); report_fp = fopen(report_file, "a"); if (report_fp != NULL) { if (verbose) fprintf(report_fp, "Opening \"%s\"...\n", report_file); } else fatal(2, "cannot open \"%s\" for appending", report_file); break; case 's': ++to_stderr; break; case 'S': to_log = 0; break; case 't': timeout = atoi(optarg); break; case 'T': if (phone_num != NULL) free(phone_num); phone_num = copy_of(optarg); break; case 'U': if (phone_num2 != NULL) free(phone_num2); phone_num2 = copy_of(optarg); break; case 'v': ++verbose; break; case 'V': ++Verbose; break; default: usage(); break; } } argc -= optind; argv += optind; /* * Default the report file to the stderr location */ if (report_fp == NULL) report_fp = stderr; if (to_log) { openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2); if (verbose) setlogmask(LOG_UPTO(LOG_INFO)); else setlogmask(LOG_UPTO(LOG_WARNING)); } if (chat_file != NULL) { if (*argv != NULL) usage(); else { init(); do_file(chat_file); } } else { init(); while (*argv != NULL && argc > 0) { chat_expect(*argv); argv++; argc--; if (*argv != NULL && argc > 0) { chat_send(*argv); argv++; argc--; } } } terminate(0); return 0; } /* * Process a chat script when read from a file. */ void do_file(char *chatfile) { int linect, sendflg; char *sp, *arg, quote; char buf [STR_LEN]; FILE *cfp; cfp = fopen (chatfile, "r"); if (cfp == NULL) fatal(1, "%s -- open failed: %m", chatfile); linect = 0; sendflg = 0; while (fgets(buf, STR_LEN, cfp) != NULL) { sp = strchr (buf, '\n'); if (sp) *sp = '\0'; linect++; sp = buf; /* lines starting with '#' are comments. If a real '#' is to be expected, it should be quoted .... */ if ( *sp == '#' ) continue; while (*sp != '\0') { if (*sp == ' ' || *sp == '\t') { ++sp; continue; } if (*sp == '"' || *sp == '\'') { quote = *sp++; arg = sp; while (*sp != quote) { if (*sp == '\0') fatal(1, "unterminated quote (line %d)", linect); if (*sp++ == '\\') { if (*sp != '\0') ++sp; } } } else { arg = sp; while (*sp != '\0' && *sp != ' ' && *sp != '\t') ++sp; } if (*sp != '\0') *sp++ = '\0'; if (sendflg) chat_send (arg); else chat_expect (arg); sendflg = !sendflg; } } fclose (cfp); } /* * We got an error parsing the command line. */ static void usage(void) { fprintf(stderr, "Usage: chat [-esSvV] [-f chat-file] [-r report-file] [-t timeout]\n" " [-T phone-number] [-U phone-number2] [chat-script]\n" "where chat-script has the form:\n" " [...[[expect[-send[-expect...]] send expect[-send[-expect]] ...]]]\n"); exit(1); } /* * Send a message to syslog and/or stderr. */ void chat_logf(const char *fmt, ...) { char line[1024]; va_list args; va_start(args, fmt); vfmtmsg(line, sizeof(line), fmt, args); va_end(args); if (to_log) syslog(LOG_INFO, "%s", line); if (to_stderr) fprintf(stderr, "%s\n", line); } /* * Print an error message and terminate. */ void fatal(int code, const char *fmt, ...) { char line[1024]; va_list args; va_start(args, fmt); vfmtmsg(line, sizeof(line), fmt, args); va_end(args); if (to_log) syslog(LOG_ERR, "%s", line); if (to_stderr) fprintf(stderr, "%s\n", line); terminate(code); } static int alarmed; SIGTYPE sigalrm(int signo __unused) { int flags; alarm(1); alarmed = 1; /* Reset alarm to avoid race window */ signal(SIGALRM, sigalrm); /* that can cause hanging in read() */ if ((flags = fcntl(0, F_GETFL, 0)) == -1) fatal(2, "Can't get file mode flags on stdin: %m"); if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1) fatal(2, "Can't set file mode flags on stdin: %m"); if (verbose) chat_logf("alarm"); } SIGTYPE sigint(int signo __unused) { fatal(2, "SIGINT"); } SIGTYPE sigterm(int signo __unused) { fatal(2, "SIGTERM"); } SIGTYPE sighup(int signo __unused) { fatal(2, "SIGHUP"); } void init(void) { signal(SIGINT, sigint); signal(SIGTERM, sigterm); signal(SIGHUP, sighup); set_tty_parameters(); signal(SIGALRM, sigalrm); alarm(0); alarmed = 0; } void set_tty_parameters(void) { #if defined(get_term_param) term_parms t; if (get_term_param (&t) < 0) fatal(2, "Can't get terminal parameters: %m"); saved_tty_parameters = t; have_tty_parameters = 1; t.c_iflag |= IGNBRK | ISTRIP | IGNPAR; t.c_oflag = 0; t.c_lflag = 0; t.c_cc[VERASE] = t.c_cc[VKILL] = 0; t.c_cc[VMIN] = 1; t.c_cc[VTIME] = 0; if (set_term_param (&t) < 0) fatal(2, "Can't set terminal parameters: %m"); #endif } void break_sequence(void) { tcsendbreak (0, 0); } void terminate(int status) { echo_stderr(-1); if (report_file != (char *) 0 && report_fp != (FILE *) NULL) { /* * Allow the last of the report string to be gathered before we terminate. */ if (report_gathering) { int c; size_t rep_len; rep_len = strlen(report_buffer); while (rep_len + 1 < sizeof(report_buffer)) { alarm(1); c = get_char(); alarm(0); if (c < 0 || iscntrl(c)) break; report_buffer[rep_len] = c; ++rep_len; } report_buffer[rep_len] = 0; fprintf (report_fp, "chat: %s\n", report_buffer); } if (verbose) fprintf (report_fp, "Closing \"%s\".\n", report_file); fclose (report_fp); report_fp = (FILE *) NULL; } #if defined(get_term_param) if (have_tty_parameters) { if (set_term_param (&saved_tty_parameters) < 0) fatal(2, "Can't restore terminal parameters: %m"); } #endif exit(status); } /* * 'Clean up' this string. */ char * clean(char *s, int sending) { char temp[STR_LEN], cur_chr; char *s1, *phchar; int add_return = sending; #define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) s1 = temp; /* Don't overflow buffer, leave room for chars we append later */ while (*s && s1 - temp < (off_t)(sizeof(temp) - 2 - add_return)) { cur_chr = *s++; if (cur_chr == '^') { cur_chr = *s++; if (cur_chr == '\0') { *s1++ = '^'; break; } cur_chr &= 0x1F; if (cur_chr != 0) { *s1++ = cur_chr; } continue; } if (cur_chr != '\\') { *s1++ = cur_chr; continue; } cur_chr = *s++; if (cur_chr == '\0') { if (sending) { *s1++ = '\\'; *s1++ = '\\'; } break; } switch (cur_chr) { case 'b': *s1++ = '\b'; break; case 'c': if (sending && *s == '\0') add_return = 0; else *s1++ = cur_chr; break; case '\\': case 'K': case 'p': case 'd': if (sending) *s1++ = '\\'; *s1++ = cur_chr; break; case 'T': if (sending && phone_num) { for ( phchar = phone_num; *phchar != '\0'; phchar++) *s1++ = *phchar; } else { *s1++ = '\\'; *s1++ = 'T'; } break; case 'U': if (sending && phone_num2) { for ( phchar = phone_num2; *phchar != '\0'; phchar++) *s1++ = *phchar; } else { *s1++ = '\\'; *s1++ = 'U'; } break; case 'q': quiet = 1; break; case 'r': *s1++ = '\r'; break; case 'n': *s1++ = '\n'; break; case 's': *s1++ = ' '; break; case 't': *s1++ = '\t'; break; case 'N': if (sending) { *s1++ = '\\'; *s1++ = '\0'; } else *s1++ = 'N'; break; default: if (isoctal (cur_chr)) { cur_chr &= 0x07; if (isoctal (*s)) { cur_chr <<= 3; cur_chr |= *s++ - '0'; if (isoctal (*s)) { cur_chr <<= 3; cur_chr |= *s++ - '0'; } } if (cur_chr != 0 || sending) { if (sending && (cur_chr == '\\' || cur_chr == 0)) *s1++ = '\\'; *s1++ = cur_chr; } break; } if (sending) *s1++ = '\\'; *s1++ = cur_chr; break; } } if (add_return) *s1++ = '\r'; *s1++ = '\0'; /* guarantee closure */ *s1++ = '\0'; /* terminate the string */ return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */ } /* * A modified version of 'strtok'. This version skips \ sequences. */ char * expect_strtok (char *s, const char *term) { static char *str = blank; int escape_flag = 0; char *result; /* * If a string was specified then do initial processing. */ if (s) str = s; /* * If this is the escape flag then reset it and ignore the character. */ if (*str) result = str; else result = (char *) 0; while (*str) { if (escape_flag) { escape_flag = 0; ++str; continue; } if (*str == '\\') { ++str; escape_flag = 1; continue; } /* * If this is not in the termination string, continue. */ if (strchr (term, *str) == (char *) 0) { ++str; continue; } /* * This is the terminator. Mark the end of the string and stop. */ *str++ = '\0'; break; } return (result); } /* * Process the expect string */ void chat_expect(char *s) { char *expect; char *reply; if (strcmp(s, "HANGUP") == 0) { ++hup_next; return; } if (strcmp(s, "ABORT") == 0) { ++abort_next; return; } if (strcmp(s, "CLR_ABORT") == 0) { ++clear_abort_next; return; } if (strcmp(s, "REPORT") == 0) { ++report_next; return; } if (strcmp(s, "CLR_REPORT") == 0) { ++clear_report_next; return; } if (strcmp(s, "TIMEOUT") == 0) { ++timeout_next; return; } if (strcmp(s, "ECHO") == 0) { ++echo_next; return; } if (strcmp(s, "SAY") == 0) { ++say_next; return; } /* * Fetch the expect and reply string. */ for (;;) { expect = expect_strtok (s, "-"); s = (char *) 0; if (expect == (char *) 0) return; reply = expect_strtok (s, "-"); /* * Handle the expect string. If successful then exit. */ if (get_string (expect)) return; /* * If there is a sub-reply string then send it. Otherwise any condition * is terminal. */ if (reply == (char *) 0 || exit_code != 3) break; chat_send (reply); } /* * The expectation did not occur. This is terminal. */ if (fail_reason) chat_logf("Failed (%s)", fail_reason); else chat_logf("Failed"); terminate(exit_code); } /* * Translate the input character to the appropriate string for printing * the data. */ char * character(int c) { static char string[10]; const char *meta; meta = (c & 0x80) ? "M-" : ""; c &= 0x7F; if (c < 32) sprintf(string, "%s^%c", meta, (int)c + '@'); else if (c == 127) sprintf(string, "%s^?", meta); else sprintf(string, "%s%c", meta, c); return (string); } /* * process the reply string */ void chat_send(char *s) { if (say_next) { say_next = 0; s = clean(s,0); write(STDERR_FILENO, s, strlen(s)); free(s); return; } if (hup_next) { hup_next = 0; if (strcmp(s, "OFF") == 0) signal(SIGHUP, SIG_IGN); else signal(SIGHUP, sighup); return; } if (echo_next) { echo_next = 0; echo = (strcmp(s, "ON") == 0); return; } if (abort_next) { char *s1; abort_next = 0; if (n_aborts >= MAX_ABORTS) fatal(2, "Too many ABORT strings"); s1 = clean(s, 0); if (strlen(s1) > strlen(s) || strlen(s1) + 1 > sizeof(fail_buffer)) fatal(1, "Illegal or too-long ABORT string ('%v')", s); abort_string[n_aborts++] = s1; if (verbose) chat_logf("abort on (%v)", s); return; } if (clear_abort_next) { char *s1; int i; int old_max; int pack = 0; clear_abort_next = 0; s1 = clean(s, 0); if (strlen(s1) > strlen(s) || strlen(s1) + 1 > sizeof(fail_buffer)) fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s); old_max = n_aborts; for (i=0; i < n_aborts; i++) { if ( strcmp(s1,abort_string[i]) == 0 ) { free(abort_string[i]); abort_string[i] = NULL; pack++; n_aborts--; if (verbose) chat_logf("clear abort on (%v)", s); } } free(s1); if (pack) pack_array(abort_string,old_max); return; } if (report_next) { char *s1; report_next = 0; if (n_reports >= MAX_REPORTS) fatal(2, "Too many REPORT strings"); s1 = clean(s, 0); if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) fatal(1, "Illegal or too-long REPORT string ('%v')", s); report_string[n_reports++] = s1; if (verbose) chat_logf("report (%v)", s); return; } if (clear_report_next) { char *s1; int i; int old_max; int pack = 0; clear_report_next = 0; s1 = clean(s, 0); if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) fatal(1, "Illegal or too-long REPORT string ('%v')", s); old_max = n_reports; for (i=0; i < n_reports; i++) { if ( strcmp(s1,report_string[i]) == 0 ) { free(report_string[i]); report_string[i] = NULL; pack++; n_reports--; if (verbose) chat_logf("clear report (%v)", s); } } free(s1); if (pack) pack_array(report_string,old_max); return; } if (timeout_next) { timeout_next = 0; timeout = atoi(s); if (timeout <= 0) timeout = DEFAULT_CHAT_TIMEOUT; if (verbose) chat_logf("timeout set to %d seconds", timeout); return; } if (strcmp(s, "EOT") == 0) s = strdup("^D\\c"); else if (strcmp(s, "BREAK") == 0) s = strdup("\\K\\c"); if (!put_string(s)) fatal(1, "Failed"); } int get_char(void) { int status; char c; status = read(STDIN_FILENO, &c, 1); switch (status) { case 1: return ((int)c & 0x7F); default: chat_logf("warning: read() on stdin returned %d", status); case -1: if ((status = fcntl(0, F_GETFL, 0)) == -1) fatal(2, "Can't get file mode flags on stdin: %m"); if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) fatal(2, "Can't set file mode flags on stdin: %m"); return (-1); } } int put_char(int c) { int status; char ch = c; usleep(10000); /* inter-character typing delay (?) */ status = write(STDOUT_FILENO, &ch, 1); switch (status) { case 1: return (0); default: chat_logf("warning: write() on stdout returned %d", status); case -1: if ((status = fcntl(0, F_GETFL, 0)) == -1) fatal(2, "Can't get file mode flags on stdin, %m"); if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) fatal(2, "Can't set file mode flags on stdin: %m"); return (-1); } } int write_char(int c) { if (alarmed || put_char(c) < 0) { alarm(0); alarmed = 0; if (verbose) { if (errno == EINTR || errno == EWOULDBLOCK) chat_logf(" -- write timed out"); else chat_logf(" -- write failed: %m"); } return (0); } return (1); } int put_string(char *s) { quiet = 0; s = clean(s, 1); if (verbose) chat_logf("send (%v)", quiet ? "??????" : s); alarm(timeout); alarmed = 0; while (*s) { char c = *s++; if (c != '\\') { if (!write_char (c)) return 0; continue; } c = *s++; switch (c) { case 'd': sleep(1); break; case 'K': break_sequence(); break; case 'p': usleep(10000); /* 1/100th of a second (arg is microseconds) */ break; default: if (!write_char (c)) return 0; break; } } alarm(0); alarmed = 0; return (1); } /* * Echo a character to stderr. * When called with -1, a '\n' character is generated when * the cursor is not at the beginning of a line. */ void echo_stderr(int n) { static int need_lf; char *s; switch (n) { case '\r': /* ignore '\r' */ break; case -1: if (need_lf == 0) break; /* FALLTHROUGH */ case '\n': write(STDERR_FILENO, "\n", 1); need_lf = 0; break; default: s = character(n); write(STDERR_FILENO, s, strlen(s)); need_lf = 1; break; } } /* * 'Wait for' this string to appear on this file descriptor. */ int get_string(char *string) { char temp[STR_LEN]; int c; size_t len, minlen; char *s = temp, *end = s + STR_LEN; char *logged = temp; fail_reason = (char *)0; if (strlen(string) > STR_LEN) { chat_logf("expect string is too long"); exit_code = 1; return 0; } string = clean(string, 0); len = strlen(string); minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1; if (verbose) chat_logf("expect (%v)", string); if (len == 0) { if (verbose) chat_logf("got it"); return (1); } alarm(timeout); alarmed = 0; while ( ! alarmed && (c = get_char()) >= 0) { int n, abort_len, report_len; if (echo) echo_stderr(c); if (verbose && c == '\n') { if (s == logged) chat_logf(""); /* blank line */ else chat_logf("%0.*v", s - logged, logged); logged = s + 1; } *s++ = c; if (verbose && s >= logged + 80) { chat_logf("%0.*v", s - logged, logged); logged = s; } if (Verbose) { if (c == '\n') fputc( '\n', stderr ); else if (c != '\r') fprintf( stderr, "%s", character(c) ); } if (!report_gathering) { for (n = 0; n < n_reports; ++n) { if ((report_string[n] != (char*) NULL) && s - temp >= (report_len = strlen(report_string[n])) && strncmp(s - report_len, report_string[n], report_len) == 0) { time_t time_now = time ((time_t*) NULL); struct tm* tm_now = localtime (&time_now); strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now); strcat (report_buffer, report_string[n]); report_string[n] = (char *) NULL; report_gathering = 1; break; } } } else { if (!iscntrl (c)) { int rep_len = strlen (report_buffer); report_buffer[rep_len] = c; report_buffer[rep_len + 1] = '\0'; } else { report_gathering = 0; fprintf (report_fp, "chat: %s\n", report_buffer); } } if ((size_t)(s - temp) >= len && c == string[len - 1] && strncmp(s - len, string, len) == 0) { if (verbose) { if (s > logged) chat_logf("%0.*v", s - logged, logged); chat_logf(" -- got it\n"); } alarm(0); alarmed = 0; return (1); } for (n = 0; n < n_aborts; ++n) { if (s - temp >= (abort_len = strlen(abort_string[n])) && strncmp(s - abort_len, abort_string[n], abort_len) == 0) { if (verbose) { if (s > logged) chat_logf("%0.*v", s - logged, logged); chat_logf(" -- failed"); } alarm(0); alarmed = 0; exit_code = n + 4; strcpy(fail_reason = fail_buffer, abort_string[n]); return (0); } } if (s >= end) { if (logged < s - minlen) { chat_logf("%0.*v", s - logged, logged); logged = s; } s -= minlen; memmove(temp, s, minlen); logged = temp + (logged - s); s = temp + minlen; } if (alarmed && verbose) chat_logf("warning: alarm synchronization problem"); } alarm(0); exit_code = 3; alarmed = 0; return (0); } void pack_array(char **array, int end) { int i, j; for (i = 0; i < end; i++) { if (array[i] == NULL) { for (j = i+1; j < end; ++j) if (array[j] != NULL) array[i++] = array[j]; for (; i < end; ++i) array[i] = NULL; break; } } } /* * vfmtmsg - format a message into a buffer. Like vsprintf except we * also specify the length of the output buffer, and we handle the * %m (error message) format. * Doesn't do floating-point formats. * Returns the number of chars put into buf. */ #define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) int vfmtmsg(char *buf, int buflen, const char *fmt, va_list args) { int c, i, n; int width, prec, fillch; int base, len, neg, quoted; unsigned long val = 0; char *str, *buf0; const char *f; unsigned char *p; char num[32]; static char hexchars[] = "0123456789abcdef"; buf0 = buf; --buflen; while (buflen > 0) { for (f = fmt; *f != '%' && *f != 0; ++f) ; if (f > fmt) { len = f - fmt; if (len > buflen) len = buflen; memcpy(buf, fmt, len); buf += len; buflen -= len; fmt = f; } if (*fmt == 0) break; c = *++fmt; width = prec = 0; fillch = ' '; if (c == '0') { fillch = '0'; c = *++fmt; } if (c == '*') { width = va_arg(args, int); c = *++fmt; } else { while (isdigit(c)) { width = width * 10 + c - '0'; c = *++fmt; } } if (c == '.') { c = *++fmt; if (c == '*') { prec = va_arg(args, int); c = *++fmt; } else { while (isdigit(c)) { prec = prec * 10 + c - '0'; c = *++fmt; } } } str = NULL; base = 0; neg = 0; ++fmt; switch (c) { case 'd': i = va_arg(args, int); if (i < 0) { neg = 1; val = -i; } else val = i; base = 10; break; case 'o': val = va_arg(args, unsigned int); base = 8; break; case 'x': val = va_arg(args, unsigned int); base = 16; break; case 'p': val = (unsigned long) va_arg(args, void *); base = 16; neg = 2; break; case 's': str = va_arg(args, char *); break; case 'c': num[0] = va_arg(args, int); num[1] = 0; str = num; break; case 'm': str = strerror(errno); break; case 'v': /* "visible" string */ case 'q': /* quoted string */ quoted = c == 'q'; p = va_arg(args, unsigned char *); if (fillch == '0' && prec > 0) { n = prec; } else { n = strlen((char *)p); if (prec > 0 && prec < n) n = prec; } while (n > 0 && buflen > 0) { c = *p++; --n; if (!quoted && c >= 0x80) { OUTCHAR('M'); OUTCHAR('-'); c -= 0x80; } if (quoted && (c == '"' || c == '\\')) OUTCHAR('\\'); if (c < 0x20 || (0x7f <= c && c < 0xa0)) { if (quoted) { OUTCHAR('\\'); switch (c) { case '\t': OUTCHAR('t'); break; case '\n': OUTCHAR('n'); break; case '\b': OUTCHAR('b'); break; case '\f': OUTCHAR('f'); break; default: OUTCHAR('x'); OUTCHAR(hexchars[c >> 4]); OUTCHAR(hexchars[c & 0xf]); } } else { if (c == '\t') OUTCHAR(c); else { OUTCHAR('^'); OUTCHAR(c ^ 0x40); } } } else OUTCHAR(c); } continue; default: *buf++ = '%'; if (c != '%') --fmt; /* so %z outputs %z etc. */ --buflen; continue; } if (base != 0) { str = num + sizeof(num); *--str = 0; while (str > num + neg) { *--str = hexchars[val % base]; val = val / base; if (--prec <= 0 && val == 0) break; } switch (neg) { case 1: *--str = '-'; break; case 2: *--str = 'x'; *--str = '0'; break; } len = num + sizeof(num) - 1 - str; } else { len = strlen(str); if (prec > 0 && len > prec) len = prec; } if (width > 0) { if (width > buflen) width = buflen; if ((n = width - len) > 0) { buflen -= n; for (; n > 0; --n) *buf++ = fillch; } } if (len > buflen) len = buflen; memcpy(buf, str, len); buf += len; buflen -= len; } *buf = 0; return buf - buf0; } diff --git a/usr.bin/chkey/chkey.c b/usr.bin/chkey/chkey.c index f5876396cb2c..8309237990f1 100644 --- a/usr.bin/chkey/chkey.c +++ b/usr.bin/chkey/chkey.c @@ -1,269 +1,266 @@ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user or with the express written consent of * Sun Microsystems, Inc. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if 0 -#endif - #include /* * Copyright (C) 1986, Sun Microsystems, Inc. */ /* * Command to change one's public key in the public key database */ #include #include #ifdef YP #include #include #else #define YPOP_STORE 4 #endif #include #include #include #include #include #include #include #include "extern.h" #ifdef YPPASSWD struct passwd *ypgetpwuid(uid_t); #endif #ifdef YP static char *domain; static char PKMAP[] = "publickey.byname"; #else static char PKFILE[] = "/etc/publickey"; #endif /* YP */ static char ROOTKEY[] = "/etc/.rootkey"; static void usage(void) __dead2; extern int yp_update(char *, char *, int, char *, size_t, char *, size_t); int main(int argc, char **argv) { char name[MAXNETNAMELEN+1]; char public[HEXKEYBYTES + 1]; char secret[HEXKEYBYTES + 1]; char crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE + 1]; char crypt2[HEXKEYBYTES + KEYCHECKSUMSIZE + 1]; int status; char *pass; struct passwd *pw; uid_t uid; int force = 0; int ch; #ifdef YP char *master; #endif #ifdef YPPASSWD char *cryptpw; #endif while ((ch = getopt(argc, argv, "f")) != -1) switch(ch) { case 'f': force = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc != 0) usage(); #ifdef YP (void)yp_get_default_domain(&domain); if (yp_master(domain, PKMAP, &master) != 0) errx(1, "can't find master of publickey database"); #endif uid = getuid() /*geteuid()*/; if (uid == 0) { if (host2netname(name, NULL, NULL) == 0) errx(1, "cannot convert hostname to netname"); } else { if (user2netname(name, uid, NULL) == 0) errx(1, "cannot convert username to netname"); } (void)printf("Generating new key for %s.\n", name); if (!force) { if (uid != 0) { #ifdef YPPASSWD pw = ypgetpwuid(uid); #else pw = getpwuid(uid); #endif if (pw == NULL) { #ifdef YPPASSWD errx(1, "no NIS password entry found: can't change key"); #else errx(1, "no password entry found: can't change key"); #endif } } else { pw = getpwuid(0); if (pw == NULL) errx(1, "no password entry found: can't change key"); } } pass = getpass("Password:"); #ifdef YPPASSWD if (!force) { cryptpw = crypt(pass, pw->pw_passwd); if (cryptpw == NULL || strcmp(cryptpw, pw->pw_passwd) != 0) errx(1, "invalid password"); } #else force = 1; /* Make this mandatory */ #endif genkeys(public, secret, pass); memcpy(crypt1, secret, HEXKEYBYTES); memcpy(crypt1 + HEXKEYBYTES, secret, KEYCHECKSUMSIZE); crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0; xencrypt(crypt1, pass); if (force) { memcpy(crypt2, crypt1, HEXKEYBYTES + KEYCHECKSUMSIZE + 1); xdecrypt(crypt2, getpass("Retype password:")); if (memcmp(crypt2, crypt2 + HEXKEYBYTES, KEYCHECKSUMSIZE) != 0 || memcmp(crypt2, secret, HEXKEYBYTES) != 0) errx(1, "password incorrect"); } #ifdef YP (void)printf("Sending key change request to %s...\n", master); #endif status = setpublicmap(name, public, crypt1); if (status != 0) { #ifdef YP errx(1, "unable to update NIS database (%u): %s", status, yperr_string(status)); #else errx(1, "unable to update publickey database"); #endif } if (uid == 0) { /* * Root users store their key in /etc/$ROOTKEY so * that they can auto reboot without having to be * around to type a password. Storing this in a file * is rather dubious: it should really be in the EEPROM * so it does not go over the net. */ int fd; fd = open(ROOTKEY, O_WRONLY|O_TRUNC|O_CREAT, 0); if (fd < 0) { warn("%s", ROOTKEY); } else { char newline = '\n'; if (write(fd, secret, strlen(secret)) < 0 || write(fd, &newline, sizeof(newline)) < 0) warn("%s: write", ROOTKEY); } close(fd); } if (key_setsecret(secret) < 0) errx(1, "unable to login with new secret key"); (void)printf("Done.\n"); exit(0); /* NOTREACHED */ } static void usage(void) { (void)fprintf(stderr, "usage: chkey [-f]\n"); exit(1); /* NOTREACHED */ } /* * Set the entry in the public key file */ int setpublicmap(char *name, char *public, char *secret) { char pkent[1024]; (void)sprintf(pkent,"%s:%s", public, secret); #ifdef YP return (yp_update(domain, PKMAP, YPOP_STORE, name, strlen(name), pkent, strlen(pkent))); #else return (localupdate(name, PKFILE, YPOP_STORE, strlen(name), name, strlen(pkent), pkent)); #endif } #ifdef YPPASSWD struct passwd * ypgetpwuid(uid_t uid) { char uidstr[10]; char *val; int vallen; static struct passwd pw; char *p; (void)sprintf(uidstr, "%d", uid); if (yp_match(domain, "passwd.byuid", uidstr, strlen(uidstr), &val, &vallen) != 0) { return (NULL); } p = strchr(val, ':'); if (p == NULL) { return (NULL); } pw.pw_passwd = p + 1; p = strchr(pw.pw_passwd, ':'); if (p == NULL) { return (NULL); } *p = 0; return (&pw); } #endif /* YPPASSWD */ diff --git a/usr.bin/chpass/edit.c b/usr.bin/chpass/edit.c index 55607f72a3c5..978a3eb1a63f 100644 --- a/usr.bin/chpass/edit.c +++ b/usr.bin/chpass/edit.c @@ -1,293 +1,289 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "chpass.h" static int display(const char *tfn, struct passwd *pw); static struct passwd *verify(const char *tfn, struct passwd *pw); struct passwd * edit(const char *tfn, struct passwd *pw) { struct passwd *npw; char *line; size_t len; if (display(tfn, pw) == -1) return (NULL); for (;;) { switch (pw_edit(1)) { case -1: return (NULL); case 0: return (pw_dup(pw)); default: break; } if ((npw = verify(tfn, pw)) != NULL) return (npw); free(npw); printf("re-edit the password file? "); fflush(stdout); if ((line = fgetln(stdin, &len)) == NULL) { warn("fgetln()"); return (NULL); } if (len > 0 && (*line == 'N' || *line == 'n')) return (NULL); } } /* * display -- * print out the file for the user to edit; strange side-effect: * set conditional flag if the user gets to edit the shell. */ static int display(const char *tfn, struct passwd *pw) { FILE *fp; char *bp, *gecos, *p; if ((fp = fopen(tfn, "w")) == NULL) { warn("%s", tfn); return (-1); } (void)fprintf(fp, "#Changing user information for %s.\n", pw->pw_name); if (master_mode) { (void)fprintf(fp, "Login: %s\n", pw->pw_name); (void)fprintf(fp, "Password: %s\n", pw->pw_passwd); (void)fprintf(fp, "Uid [#]: %lu\n", (unsigned long)pw->pw_uid); (void)fprintf(fp, "Gid [# or name]: %lu\n", (unsigned long)pw->pw_gid); (void)fprintf(fp, "Change [month day year]: %s\n", ttoa(pw->pw_change)); (void)fprintf(fp, "Expire [month day year]: %s\n", ttoa(pw->pw_expire)); (void)fprintf(fp, "Class: %s\n", pw->pw_class); (void)fprintf(fp, "Home directory: %s\n", pw->pw_dir); (void)fprintf(fp, "Shell: %s\n", *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); } /* Only admin can change "restricted" shells. */ #if 0 else if (ok_shell(pw->pw_shell)) /* * Make shell a restricted field. Ugly with a * necklace, but there's not much else to do. */ #else else if ((!list[E_SHELL].restricted && ok_shell(pw->pw_shell)) || master_mode) /* * If change not restrict (table.c) and standard shell * OR if root, then allow editing of shell. */ #endif (void)fprintf(fp, "Shell: %s\n", *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); else list[E_SHELL].restricted = 1; if ((bp = gecos = strdup(pw->pw_gecos)) == NULL) { warn(NULL); fclose(fp); return (-1); } p = strsep(&bp, ","); p = strdup(p ? p : ""); list[E_NAME].save = p; if (!list[E_NAME].restricted || master_mode) (void)fprintf(fp, "Full Name: %s\n", p); p = strsep(&bp, ","); p = strdup(p ? p : ""); list[E_LOCATE].save = p; if (!list[E_LOCATE].restricted || master_mode) (void)fprintf(fp, "Office Location: %s\n", p); p = strsep(&bp, ","); p = strdup(p ? p : ""); list[E_BPHONE].save = p; if (!list[E_BPHONE].restricted || master_mode) (void)fprintf(fp, "Office Phone: %s\n", p); p = strsep(&bp, ","); p = strdup(p ? p : ""); list[E_HPHONE].save = p; if (!list[E_HPHONE].restricted || master_mode) (void)fprintf(fp, "Home Phone: %s\n", p); bp = strdup(bp ? bp : ""); list[E_OTHER].save = bp; if (!list[E_OTHER].restricted || master_mode) (void)fprintf(fp, "Other information: %s\n", bp); free(gecos); (void)fchown(fileno(fp), getuid(), getgid()); (void)fclose(fp); return (0); } static struct passwd * verify(const char *tfn, struct passwd *pw) { struct passwd *npw; ENTRY *ep; char *buf, *p, *val; struct stat sb; FILE *fp; int line; size_t len; if ((pw = pw_dup(pw)) == NULL) return (NULL); if ((fp = fopen(tfn, "r")) == NULL || fstat(fileno(fp), &sb) == -1) { warn("%s", tfn); free(pw); return (NULL); } if (sb.st_size == 0) { warnx("corrupted temporary file"); fclose(fp); free(pw); return (NULL); } val = NULL; for (line = 1; (buf = fgetln(fp, &len)) != NULL; ++line) { if (*buf == '\0' || *buf == '#') continue; while (len > 0 && isspace(buf[len - 1])) --len; for (ep = list;; ++ep) { if (!ep->prompt) { warnx("%s: unrecognized field on line %d", tfn, line); goto bad; } if (ep->len > len) continue; if (strncasecmp(buf, ep->prompt, ep->len) != 0) continue; if (ep->restricted && !master_mode) { warnx("%s: you may not change the %s field", tfn, ep->prompt); goto bad; } for (p = buf; p < buf + len && *p != ':'; ++p) /* nothing */ ; if (*p != ':') { warnx("%s: line %d corrupted", tfn, line); goto bad; } while (++p < buf + len && isspace(*p)) /* nothing */ ; free(val); asprintf(&val, "%.*s", (int)(buf + len - p), p); if (val == NULL) goto bad; if (ep->except && strpbrk(val, ep->except)) { warnx("%s: invalid character in \"%s\" field '%s'", tfn, ep->prompt, val); goto bad; } if ((ep->func)(val, pw, ep)) goto bad; break; } } free(val); fclose(fp); /* Build the gecos field. */ len = asprintf(&p, "%s,%s,%s,%s,%s", list[E_NAME].save, list[E_LOCATE].save, list[E_BPHONE].save, list[E_HPHONE].save, list[E_OTHER].save); if (p == NULL) { warn("asprintf()"); free(pw); return (NULL); } while (len > 0 && p[len - 1] == ',') p[--len] = '\0'; pw->pw_gecos = p; buf = pw_make(pw); free(pw); free(p); if (buf == NULL) { warn("pw_make()"); return (NULL); } npw = pw_scan(buf, PWSCAN_WARN|PWSCAN_MASTER); free(buf); return (npw); bad: free(pw); free(val); fclose(fp); return (NULL); } diff --git a/usr.bin/chpass/field.c b/usr.bin/chpass/field.c index 80dcdfacea7f..b64f33e06fdb 100644 --- a/usr.bin/chpass/field.c +++ b/usr.bin/chpass/field.c @@ -1,258 +1,254 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1988, 1993, 1994 * The Regents of the University of California. All rights reserved. * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include "chpass.h" /* ARGSUSED */ int p_login(char *p, struct passwd *pw, ENTRY *ep __unused) { if (!*p) { warnx("empty login field"); return (-1); } if (*p == '-') { warnx("login names may not begin with a hyphen"); return (-1); } if (!(pw->pw_name = strdup(p))) { warnx("can't save entry"); return (-1); } if (strchr(p, '.')) warnx("\'.\' is dangerous in a login name"); for (; *p; ++p) if (isupper(*p)) { warnx("upper-case letters are dangerous in a login name"); break; } return (0); } /* ARGSUSED */ int p_passwd(char *p, struct passwd *pw, ENTRY *ep __unused) { if (!(pw->pw_passwd = strdup(p))) { warnx("can't save password entry"); return (-1); } return (0); } /* ARGSUSED */ int p_uid(char *p, struct passwd *pw, ENTRY *ep __unused) { uid_t id; char *np; if (!*p) { warnx("empty uid field"); return (-1); } if (!isdigit(*p)) { warnx("illegal uid"); return (-1); } errno = 0; id = strtoul(p, &np, 10); if (*np || (id == (uid_t)ULONG_MAX && errno == ERANGE)) { warnx("illegal uid"); return (-1); } pw->pw_uid = id; return (0); } /* ARGSUSED */ int p_gid(char *p, struct passwd *pw, ENTRY *ep __unused) { struct group *gr; gid_t id; char *np; if (!*p) { warnx("empty gid field"); return (-1); } if (!isdigit(*p)) { if (!(gr = getgrnam(p))) { warnx("unknown group %s", p); return (-1); } pw->pw_gid = gr->gr_gid; return (0); } errno = 0; id = strtoul(p, &np, 10); if (*np || (id == (uid_t)ULONG_MAX && errno == ERANGE)) { warnx("illegal gid"); return (-1); } pw->pw_gid = id; return (0); } /* ARGSUSED */ int p_class(char *p, struct passwd *pw, ENTRY *ep __unused) { if (!(pw->pw_class = strdup(p))) { warnx("can't save entry"); return (-1); } return (0); } /* ARGSUSED */ int p_change(char *p, struct passwd *pw, ENTRY *ep __unused) { if (!atot(p, &pw->pw_change)) return (0); warnx("illegal date for change field"); return (-1); } /* ARGSUSED */ int p_expire(char *p, struct passwd *pw, ENTRY *ep __unused) { if (!atot(p, &pw->pw_expire)) return (0); warnx("illegal date for expire field"); return (-1); } /* ARGSUSED */ int p_gecos(char *p, struct passwd *pw __unused, ENTRY *ep) { if (!(ep->save = strdup(p))) { warnx("can't save entry"); return (-1); } return (0); } /* ARGSUSED */ int p_hdir(char *p, struct passwd *pw, ENTRY *ep __unused) { if (!*p) { warnx("empty home directory field"); return (-1); } if (!(pw->pw_dir = strdup(p))) { warnx("can't save entry"); return (-1); } return (0); } /* ARGSUSED */ int p_shell(char *p, struct passwd *pw, ENTRY *ep __unused) { struct stat sbuf; if (!*p) { pw->pw_shell = strdup(_PATH_BSHELL); return (0); } /* only admin can change from or to "restricted" shells */ if (!master_mode && pw->pw_shell && !ok_shell(pw->pw_shell)) { warnx("%s: current shell non-standard", pw->pw_shell); return (-1); } if (!ok_shell(p)) { if (!master_mode) { warnx("%s: non-standard shell", p); return (-1); } pw->pw_shell = strdup(p); } else pw->pw_shell = dup_shell(p); if (!pw->pw_shell) { warnx("can't save entry"); return (-1); } if (stat(pw->pw_shell, &sbuf) < 0) { if (errno == ENOENT) warnx("WARNING: shell '%s' does not exist", pw->pw_shell); else warn("WARNING: can't stat shell '%s'", pw->pw_shell); return (0); } if (!S_ISREG(sbuf.st_mode)) { warnx("WARNING: shell '%s' is not a regular file", pw->pw_shell); return (0); } if ((sbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) == 0) { warnx("WARNING: shell '%s' is not executable", pw->pw_shell); return (0); } return (0); } diff --git a/usr.bin/chpass/table.c b/usr.bin/chpass/table.c index 2d9a449578bc..53ed54e825af 100644 --- a/usr.bin/chpass/table.c +++ b/usr.bin/chpass/table.c @@ -1,59 +1,56 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif -#include #include #include #include "chpass.h" ENTRY list[] = { { "login", p_login, 1, 5, ": ", NULL }, { "password", p_passwd, 1, 8, ": ", NULL }, { "uid", p_uid, 1, 3, ": ", NULL }, { "gid", p_gid, 1, 3, ": ", NULL }, { "class", p_class, 1, 5, ": ", NULL }, { "change", p_change, 1, 6, NULL, NULL }, { "expire", p_expire, 1, 6, NULL, NULL }, #ifdef RESTRICT_FULLNAME_CHANGE /* do not allow fullname changes */ { "full name", p_gecos, 1, 9, ":,", NULL }, #else { "full name", p_gecos, 0, 9, ":,", NULL }, #endif { "office phone", p_gecos, 0, 12, ":,", NULL }, { "home phone", p_gecos, 0, 10, ":,", NULL }, { "office location", p_gecos, 0, 15, ":,", NULL }, { "other information", p_gecos, 0, 11, ": ", NULL }, { "home directory", p_hdir, 1, 14, ": ", NULL }, { "shell", p_shell, 0, 5, ": ", NULL }, { NULL, NULL, 0, 0, NULL, NULL }, }; diff --git a/usr.bin/chpass/util.c b/usr.bin/chpass/util.c index 369eaba16acc..9e3b3f7903c7 100644 --- a/usr.bin/chpass/util.c +++ b/usr.bin/chpass/util.c @@ -1,191 +1,188 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1988, 1993, 1994 * The Regents of the University of California. All rights reserved. * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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 -#endif /* not lint */ -#include #include #include #include #include #include #include #include #include #include "chpass.h" static const char *months[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", NULL }; char * ttoa(time_t tval) { struct tm *tp; static char tbuf[50]; if (tval) { tp = localtime(&tval); (void)sprintf(tbuf, "%s %d, %d", months[tp->tm_mon], tp->tm_mday, tp->tm_year + 1900); } else *tbuf = '\0'; return (tbuf); } int atot(char *p, time_t *store) { static struct tm *lt; char *t; const char **mp; time_t tval; int day, month, year; if (!*p) { *store = 0; return (0); } if (!lt) { unsetenv("TZ"); (void)time(&tval); lt = localtime(&tval); } if (!(t = strtok(p, " \t"))) goto bad; if (isdigit(*t)) { month = atoi(t); } else { for (mp = months;; ++mp) { if (!*mp) goto bad; if (!strncasecmp(*mp, t, 3)) { month = mp - months + 1; break; } } } if (!(t = strtok(NULL, " \t,")) || !isdigit(*t)) goto bad; day = atoi(t); if (!(t = strtok(NULL, " \t,")) || !isdigit(*t)) goto bad; year = atoi(t); if (day < 1 || day > 31 || month < 1 || month > 12) goto bad; /* Allow two digit years 1969-2068 */ if (year < 69) year += 2000; else if (year < 100) year += 1900; if (year < 1969) bad: return (1); lt->tm_year = year - 1900; lt->tm_mon = month - 1; lt->tm_mday = day; lt->tm_hour = 0; lt->tm_min = 0; lt->tm_sec = 0; lt->tm_isdst = -1; if ((tval = mktime(lt)) < 0) return (1); #ifndef __i386__ /* * PR227589: The pwd.db and spwd.db files store the change and expire * dates as unsigned 32-bit ints which overflow in 2106, so larger * values must be rejected until the introduction of a v5 password * database. i386 has 32-bit time_t and so dates beyond y2038 are * already rejected by mktime above. */ if (tval > UINT32_MAX) return (1); #endif *store = tval; return (0); } int ok_shell(char *name) { char *p, *sh; setusershell(); while ((sh = getusershell())) { if (!strcmp(name, sh)) { endusershell(); return (1); } /* allow just shell name, but use "real" path */ if ((p = strrchr(sh, '/')) && strcmp(name, p + 1) == 0) { endusershell(); return (1); } } endusershell(); return (0); } char * dup_shell(char *name) { char *p, *sh, *ret; setusershell(); while ((sh = getusershell())) { if (!strcmp(name, sh)) { endusershell(); return (strdup(name)); } /* allow just shell name, but use "real" path */ if ((p = strrchr(sh, '/')) && strcmp(name, p + 1) == 0) { ret = strdup(sh); endusershell(); return (ret); } } endusershell(); return (NULL); } diff --git a/usr.bin/cksum/crc.c b/usr.bin/cksum/crc.c index 6c91e9f2cb5d..1a49e1526874 100644 --- a/usr.bin/cksum/crc.c +++ b/usr.bin/cksum/crc.c @@ -1,140 +1,137 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * James W. Williams of NASA Goddard Space Flight Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include #include #include #include "extern.h" static const uint32_t crctab[] = { 0x0, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 }; /* * Compute a POSIX 1003.2 checksum. This routine has been broken out so that * other programs can use it. It takes a file descriptor to read from and * locations to store the crc and the number of bytes read. It returns 0 on * success and 1 on failure. Errno is set on failure. */ static uint32_t crc_total = ~0; /* The crc over a number of files. */ int crc(int fd, uint32_t *cval, off_t *clen) { uint32_t lcrc; int nr; off_t len; u_char *p; u_char buf[16 * 1024]; #define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] lcrc = len = 0; crc_total = ~crc_total; while ((nr = read(fd, buf, sizeof(buf))) > 0) for (len += nr, p = buf; nr--; ++p) { COMPUTE(lcrc, *p); COMPUTE(crc_total, *p); } if (nr < 0) return (1); *clen = len; /* Include the length of the file. */ for (; len != 0; len >>= 8) { COMPUTE(lcrc, len & 0xff); COMPUTE(crc_total, len & 0xff); } *cval = ~lcrc; crc_total = ~crc_total; return (0); } diff --git a/usr.bin/cksum/crc32.c b/usr.bin/cksum/crc32.c index 646704dcea47..2eb3e1b7d190 100644 --- a/usr.bin/cksum/crc32.c +++ b/usr.bin/cksum/crc32.c @@ -1,120 +1,119 @@ /* * This code implements the AUTODIN II polynomial used by Ethernet, * and can be used to calculate multicast address hash indices. * It assumes that the low order bits will be transmitted first, * and consequently the low byte should be sent first when * the crc computation is finished. The crc should be complemented * before transmission. * The variable corresponding to the macro argument "crc" should * be an unsigned long and should be preset to all ones for Ethernet * use. An error-free packet will leave 0xDEBB20E3 in the crc. * Spencer Garrett */ -#include #include #include #include #include #include "extern.h" #define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff]) /* generated using the AUTODIN II polynomial * x^32 + x^26 + x^23 + x^22 + x^16 + * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 */ static const uint32_t crctab[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, }; static uint32_t crc32_total = 0; int crc32(int fd, uint32_t *cval, off_t *clen) { uint32_t lcrc = ~0; int nr ; off_t len ; char buf[BUFSIZ], *p ; len = 0 ; crc32_total = ~crc32_total ; while ((nr = read(fd, buf, sizeof(buf))) > 0) for (len += nr, p = buf; nr--; ++p) { CRC(lcrc, *p) ; CRC(crc32_total, *p) ; } if (nr < 0) return 1 ; *clen = len ; *cval = ~lcrc ; crc32_total = ~crc32_total ; return 0 ; } diff --git a/usr.bin/cksum/print.c b/usr.bin/cksum/print.c index b86e174a10e9..345b36b64c44 100644 --- a/usr.bin/cksum/print.c +++ b/usr.bin/cksum/print.c @@ -1,68 +1,64 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ - -#include #include #include #include #include "extern.h" void pcrc(char *fn, uint32_t val, off_t len) { (void)printf("%lu %jd", (u_long)val, (intmax_t)len); if (fn != NULL) (void)printf(" %s", fn); (void)printf("\n"); } void psum1(char *fn, uint32_t val, off_t len) { (void)printf("%lu %jd", (u_long)val, (intmax_t)(len + 1023) / 1024); if (fn != NULL) (void)printf(" %s", fn); (void)printf("\n"); } void psum2(char *fn, uint32_t val, off_t len) { (void)printf("%lu %jd", (u_long)val, (intmax_t)(len + 511) / 512); if (fn != NULL) (void)printf(" %s", fn); (void)printf("\n"); } diff --git a/usr.bin/cksum/sum1.c b/usr.bin/cksum/sum1.c index da710cb2e90a..6174bae00adf 100644 --- a/usr.bin/cksum/sum1.c +++ b/usr.bin/cksum/sum1.c @@ -1,69 +1,65 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ - -#include #include #include #include #include "extern.h" int csum1(int fd, uint32_t *cval, off_t *clen) { int nr; u_int lcrc; off_t total; u_char *p; u_char buf[8192]; /* * 16-bit checksum, rotating right before each addition; * overflow is discarded. */ lcrc = total = 0; while ((nr = read(fd, buf, sizeof(buf))) > 0) for (total += nr, p = buf; nr--; ++p) { if (lcrc & 1) lcrc |= 0x10000; lcrc = ((lcrc >> 1) + *p) & 0xffff; } if (nr < 0) return (1); *cval = lcrc; *clen = total; return (0); } diff --git a/usr.bin/cksum/sum2.c b/usr.bin/cksum/sum2.c index 26572076943f..ae74226e5266 100644 --- a/usr.bin/cksum/sum2.c +++ b/usr.bin/cksum/sum2.c @@ -1,71 +1,68 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include #include #include #include "extern.h" int csum2(int fd, uint32_t *cval, off_t *clen) { uint32_t lcrc; int nr; off_t total; u_char *p; u_char buf[8192]; /* * Draft 8 POSIX 1003.2: * * s = sum of all bytes * r = s % 2^16 + (s % 2^32) / 2^16 * lcrc = (r % 2^16) + r / 2^16 */ lcrc = total = 0; while ((nr = read(fd, buf, sizeof(buf))) > 0) for (total += nr, p = buf; nr--; ++p) lcrc += *p; if (nr < 0) return (1); lcrc = (lcrc & 0xffff) + (lcrc >> 16); lcrc = (lcrc & 0xffff) + (lcrc >> 16); *cval = lcrc; *clen = total; return (0); } diff --git a/usr.bin/cmp/link.c b/usr.bin/cmp/link.c index 550273cb471a..33b0fa46458c 100644 --- a/usr.bin/cmp/link.c +++ b/usr.bin/cmp/link.c @@ -1,100 +1,99 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2005 Brian Somers * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include "extern.h" void c_link(const char *file1, off_t skip1, const char *file2, off_t skip2, off_t limit) { char buf1[PATH_MAX], *p1; char buf2[PATH_MAX], *p2; int dfound, len1, len2; off_t byte; u_char ch; if ((len1 = readlink(file1, buf1, sizeof(buf1) - 1)) < 0) { if (!sflag) err(ERR_EXIT, "%s", file1); else exit(ERR_EXIT); } if ((len2 = readlink(file2, buf2, sizeof(buf2) - 1)) < 0) { if (!sflag) err(ERR_EXIT, "%s", file2); else exit(ERR_EXIT); } if (skip1 > len1) skip1 = len1; buf1[len1] = '\0'; if (skip2 > len2) skip2 = len2; buf2[len2] = '\0'; dfound = 0; byte = 1; for (p1 = buf1 + skip1, p2 = buf2 + skip2; *p1 && *p2 && (limit == 0 || byte <= limit); p1++, p2++) { if ((ch = *p1) != *p2) { if (xflag) { dfound = 1; (void)printf("%08llx %02x %02x\n", (long long)byte - 1, ch, *p2); } else if (lflag) { dfound = 1; if (bflag) (void)printf("%6lld %3o %c %3o %c\n", (long long)byte, ch, ch, *p2, *p2); else (void)printf("%6lld %3o %3o\n", (long long)byte, ch, *p2); } else diffmsg(file1, file2, byte, 1, ch, *p2); /* NOTREACHED */ } byte++; } if (*p1 || *p2) eofmsg (*p1 ? file2 : file1); if (dfound) exit(DIFF_EXIT); } diff --git a/usr.bin/cmp/misc.c b/usr.bin/cmp/misc.c index 35922deee42d..78b431b6f6a9 100644 --- a/usr.bin/cmp/misc.c +++ b/usr.bin/cmp/misc.c @@ -1,70 +1,66 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #include "extern.h" void eofmsg(const char *file) { if (!sflag) warnx("EOF on %s", file); exit(DIFF_EXIT); } void diffmsg(const char *file1, const char *file2, off_t byte, off_t line, int b1, int b2) { if (sflag) goto out; if (bflag) { (void)printf("%s %s differ: char %lld, line %lld is %3o %c %3o %c\n", file1, file2, (long long)byte, (long long)line, b1, b1, b2, b2); } else { (void)printf("%s %s differ: char %lld, line %lld\n", file1, file2, (long long)byte, (long long)line); } out: exit(DIFF_EXIT); } diff --git a/usr.bin/cmp/regular.c b/usr.bin/cmp/regular.c index 38d1110dbd3a..0b78b83968c7 100644 --- a/usr.bin/cmp/regular.c +++ b/usr.bin/cmp/regular.c @@ -1,191 +1,187 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include "extern.h" static u_char *remmap(u_char *, int, off_t); static void segv_handler(int); #define MMAP_CHUNK (8*1024*1024) #define ROUNDPAGE(i) ((i) & ~pagemask) void c_regular(int fd1, const char *file1, off_t skip1, off_t len1, int fd2, const char *file2, off_t skip2, off_t len2, off_t limit) { struct sigaction act, oact; cap_rights_t rights; u_char ch, *p1, *p2, *m1, *m2, *e1, *e2; off_t byte, length, line; off_t pagemask, off1, off2; size_t pagesize; int dfound; if (skip1 > len1) eofmsg(file1); len1 -= skip1; if (skip2 > len2) eofmsg(file2); len2 -= skip2; if (sflag && len1 != len2) exit(DIFF_EXIT); pagesize = getpagesize(); pagemask = (off_t)pagesize - 1; off1 = ROUNDPAGE(skip1); off2 = ROUNDPAGE(skip2); length = MIN(len1, len2); if (limit > 0) length = MIN(length, limit); if ((m1 = remmap(NULL, fd1, off1)) == NULL) { c_special(fd1, file1, skip1, fd2, file2, skip2, limit); return; } if ((m2 = remmap(NULL, fd2, off2)) == NULL) { munmap(m1, MMAP_CHUNK); c_special(fd1, file1, skip1, fd2, file2, skip2, limit); return; } if (caph_rights_limit(fd1, cap_rights_init(&rights, CAP_MMAP_R)) < 0) err(1, "unable to limit rights for %s", file1); if (caph_rights_limit(fd2, cap_rights_init(&rights, CAP_MMAP_R)) < 0) err(1, "unable to limit rights for %s", file2); if (caph_enter() < 0) err(ERR_EXIT, "unable to enter capability mode"); sigemptyset(&act.sa_mask); act.sa_flags = SA_NODEFER; act.sa_handler = segv_handler; if (sigaction(SIGSEGV, &act, &oact)) err(ERR_EXIT, "sigaction()"); dfound = 0; e1 = m1 + MMAP_CHUNK; e2 = m2 + MMAP_CHUNK; p1 = m1 + (skip1 - off1); p2 = m2 + (skip2 - off2); for (byte = line = 1; length--; ++byte) { #ifdef SIGINFO if (info) { (void)fprintf(stderr, "%s %s char %zu line %zu\n", file1, file2, (size_t)byte, (size_t)line); info = 0; } #endif if ((ch = *p1) != *p2) { if (xflag) { dfound = 1; (void)printf("%08llx %02x %02x\n", (long long)byte - 1, ch, *p2); } else if (lflag) { dfound = 1; if (bflag) (void)printf("%6lld %3o %c %3o %c\n", (long long)byte, ch, ch, *p2, *p2); else (void)printf("%6lld %3o %3o\n", (long long)byte, ch, *p2); } else diffmsg(file1, file2, byte, line, ch, *p2); /* NOTREACHED */ } if (ch == '\n') ++line; if (++p1 == e1) { off1 += MMAP_CHUNK; if ((p1 = m1 = remmap(m1, fd1, off1)) == NULL) { munmap(m2, MMAP_CHUNK); err(ERR_EXIT, "remmap %s", file1); } e1 = m1 + MMAP_CHUNK; } if (++p2 == e2) { off2 += MMAP_CHUNK; if ((p2 = m2 = remmap(m2, fd2, off2)) == NULL) { munmap(m1, MMAP_CHUNK); err(ERR_EXIT, "remmap %s", file2); } e2 = m2 + MMAP_CHUNK; } } munmap(m1, MMAP_CHUNK); munmap(m2, MMAP_CHUNK); if (sigaction(SIGSEGV, &oact, NULL)) err(ERR_EXIT, "sigaction()"); if (len1 != len2) eofmsg (len1 > len2 ? file2 : file1); if (dfound) exit(DIFF_EXIT); } static u_char * remmap(u_char *mem, int fd, off_t offset) { if (mem != NULL) munmap(mem, MMAP_CHUNK); mem = mmap(NULL, MMAP_CHUNK, PROT_READ, MAP_SHARED, fd, offset); if (mem == MAP_FAILED) return (NULL); madvise(mem, MMAP_CHUNK, MADV_SEQUENTIAL); return (mem); } static void segv_handler(int sig __unused) { static const char msg[] = "cmp: Input/output error (caught SIGSEGV)\n"; write(STDERR_FILENO, msg, sizeof(msg)); _exit(EXIT_FAILURE); } diff --git a/usr.bin/cmp/special.c b/usr.bin/cmp/special.c index 5ca1e64f3e09..f29d3b454f64 100644 --- a/usr.bin/cmp/special.c +++ b/usr.bin/cmp/special.c @@ -1,125 +1,121 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #include "extern.h" void c_special(int fd1, const char *file1, off_t skip1, int fd2, const char *file2, off_t skip2, off_t limit) { int ch1, ch2; off_t byte, line; FILE *fp1, *fp2; int dfound; if (caph_limit_stream(fd1, CAPH_READ) < 0) err(ERR_EXIT, "caph_limit_stream(%s)", file1); if (caph_limit_stream(fd2, CAPH_READ) < 0) err(ERR_EXIT, "caph_limit_stream(%s)", file2); if (caph_enter() < 0) err(ERR_EXIT, "unable to enter capability mode"); if ((fp1 = fdopen(fd1, "r")) == NULL) err(ERR_EXIT, "%s", file1); (void)setvbuf(fp1, NULL, _IOFBF, 65536); if ((fp2 = fdopen(fd2, "r")) == NULL) err(ERR_EXIT, "%s", file2); (void)setvbuf(fp2, NULL, _IOFBF, 65536); dfound = 0; while (skip1--) if (getc(fp1) == EOF) goto eof; while (skip2--) if (getc(fp2) == EOF) goto eof; for (byte = line = 1; limit == 0 || byte <= limit; ++byte) { #ifdef SIGINFO if (info) { (void)fprintf(stderr, "%s %s char %zu line %zu\n", file1, file2, (size_t)byte, (size_t)line); info = 0; } #endif ch1 = getc(fp1); ch2 = getc(fp2); if (ch1 == EOF || ch2 == EOF) break; if (ch1 != ch2) { if (xflag) { dfound = 1; (void)printf("%08llx %02x %02x\n", (long long)byte - 1, ch1, ch2); } else if (lflag) { dfound = 1; if (bflag) (void)printf("%6lld %3o %c %3o %c\n", (long long)byte, ch1, ch1, ch2, ch2); else (void)printf("%6lld %3o %3o\n", (long long)byte, ch1, ch2); } else { diffmsg(file1, file2, byte, line, ch1, ch2); /* NOTREACHED */ } } if (ch1 == '\n') ++line; } eof: if (ferror(fp1)) err(ERR_EXIT, "%s", file1); if (ferror(fp2)) err(ERR_EXIT, "%s", file2); if (feof(fp1)) { if (!feof(fp2)) eofmsg(file1); } else if (feof(fp2)) eofmsg(file2); fclose(fp2); fclose(fp1); if (dfound) exit(DIFF_EXIT); } diff --git a/usr.bin/csplit/csplit.c b/usr.bin/csplit/csplit.c index dd645396948d..59aa7eb3d2d9 100644 --- a/usr.bin/csplit/csplit.c +++ b/usr.bin/csplit/csplit.c @@ -1,469 +1,468 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2002 Tim J. Robbins. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * csplit -- split files based on context * * This utility splits its input into numbered output files by line number * or by a regular expression. Regular expression matches have an optional * offset with them, allowing the split to occur a specified number of * lines before or after the match. * * To handle negative offsets, we stop reading when the match occurs and * store the offset that the file should have been split at, then use * this output file as input until all the "overflowed" lines have been read. * The file is then closed and truncated to the correct length. * * We assume that the output files can be seeked upon (ie. they cannot be * symlinks to named pipes or character devices), but make no such * assumption about the input. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include static void cleanup(void); static void do_lineno(const char *); static void do_rexp(const char *); static char *get_line(void); static void handlesig(int); static FILE *newfile(void); static void toomuch(FILE *, long); static void usage(void); /* * Command line options */ static const char *prefix; /* File name prefix */ static long sufflen; /* Number of decimal digits for suffix */ static int sflag; /* Suppress output of file names */ static int kflag; /* Keep output if error occurs */ /* * Other miscellaneous globals (XXX too many) */ static long lineno; /* Current line number in input file */ static long reps; /* Number of repetitions for this pattern */ static long nfiles; /* Number of files output so far */ static long maxfiles; /* Maximum number of files we can create */ static char currfile[PATH_MAX]; /* Current output file */ static const char *infn; /* Name of the input file */ static FILE *infile; /* Input file handle */ static FILE *overfile; /* Overflow file for toomuch() */ static off_t truncofs; /* Offset this file should be truncated at */ static int doclean; /* Should cleanup() remove output? */ int main(int argc, char *argv[]) { struct sigaction sa; long i; int ch; const char *expr; char *ep, *p; FILE *ofp; setlocale(LC_ALL, ""); kflag = sflag = 0; prefix = "xx"; sufflen = 2; while ((ch = getopt(argc, argv, "ksf:n:")) > 0) { switch (ch) { case 'f': prefix = optarg; break; case 'k': kflag = 1; break; case 'n': errno = 0; sufflen = strtol(optarg, &ep, 10); if (sufflen <= 0 || *ep != '\0' || errno != 0) errx(1, "%s: bad suffix length", optarg); break; case 's': sflag = 1; break; default: usage(); /*NOTREACHED*/ } } if (sufflen + strlen(prefix) >= PATH_MAX) errx(1, "name too long"); argc -= optind; argv += optind; if ((infn = *argv++) == NULL) usage(); if (strcmp(infn, "-") == 0) { infile = stdin; infn = "stdin"; } else if ((infile = fopen(infn, "r")) == NULL) err(1, "%s", infn); if (!kflag) { doclean = 1; atexit(cleanup); sa.sa_flags = 0; sa.sa_handler = handlesig; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGHUP); sigaddset(&sa.sa_mask, SIGINT); sigaddset(&sa.sa_mask, SIGTERM); sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); } lineno = 0; nfiles = 0; truncofs = 0; overfile = NULL; /* Ensure 10^sufflen < LONG_MAX. */ for (maxfiles = 1, i = 0; i < sufflen; i++) { if (maxfiles > LONG_MAX / 10) errx(1, "%ld: suffix too long (limit %ld)", sufflen, i); maxfiles *= 10; } /* Create files based on supplied patterns. */ while (nfiles < maxfiles - 1 && (expr = *argv++) != NULL) { /* Look ahead & see if this pattern has any repetitions. */ if (*argv != NULL && **argv == '{') { errno = 0; reps = strtol(*argv + 1, &ep, 10); if (reps < 0 || *ep != '}' || errno != 0) errx(1, "%s: bad repetition count", *argv + 1); argv++; } else reps = 0; if (*expr == '/' || *expr == '%') { do do_rexp(expr); while (reps-- != 0 && nfiles < maxfiles - 1); } else if (isdigit((unsigned char)*expr)) do_lineno(expr); else errx(1, "%s: unrecognised pattern", expr); } /* Copy the rest into a new file. */ if (!feof(infile)) { ofp = newfile(); while ((p = get_line()) != NULL && fputs(p, ofp) != EOF) ; if (!sflag) printf("%jd\n", (intmax_t)ftello(ofp)); if (fclose(ofp) != 0) err(1, "%s", currfile); } toomuch(NULL, 0); doclean = 0; return (0); } static void usage(void) { fprintf(stderr, "usage: csplit [-ks] [-f prefix] [-n number] file args ...\n"); exit(1); } static void handlesig(int sig __unused) { const char msg[] = "csplit: caught signal, cleaning up\n"; write(STDERR_FILENO, msg, sizeof(msg) - 1); cleanup(); _exit(2); } /* Create a new output file. */ static FILE * newfile(void) { FILE *fp; if ((size_t)snprintf(currfile, sizeof(currfile), "%s%0*ld", prefix, (int)sufflen, nfiles) >= sizeof(currfile)) errc(1, ENAMETOOLONG, NULL); if ((fp = fopen(currfile, "w+")) == NULL) err(1, "%s", currfile); nfiles++; return (fp); } /* Remove partial output, called before exiting. */ static void cleanup(void) { char fnbuf[PATH_MAX]; long i; if (!doclean) return; /* * NOTE: One cannot portably assume to be able to call snprintf() * from inside a signal handler. It does, however, appear to be safe * to do on FreeBSD. The solution to this problem is worse than the * problem itself. */ for (i = 0; i < nfiles; i++) { snprintf(fnbuf, sizeof(fnbuf), "%s%0*ld", prefix, (int)sufflen, i); unlink(fnbuf); } } /* Read a line from the input into a static buffer. */ static char * get_line(void) { static char lbuf[LINE_MAX]; FILE *src; src = overfile != NULL ? overfile : infile; again: if (fgets(lbuf, sizeof(lbuf), src) == NULL) { if (src == overfile) { src = infile; goto again; } return (NULL); } if (ferror(src)) err(1, "%s", infn); lineno++; return (lbuf); } /* Conceptually rewind the input (as obtained by get_line()) back `n' lines. */ static void toomuch(FILE *ofp, long n) { char buf[BUFSIZ]; size_t i, nread; if (overfile != NULL) { /* * Truncate the previous file we overflowed into back to * the correct length, close it. */ if (fflush(overfile) != 0) err(1, "overflow"); if (ftruncate(fileno(overfile), truncofs) != 0) err(1, "overflow"); if (fclose(overfile) != 0) err(1, "overflow"); overfile = NULL; } if (n == 0) /* Just tidying up */ return; lineno -= n; /* * Wind the overflow file backwards to `n' lines before the * current one. */ do { if (ftello(ofp) < (off_t)sizeof(buf)) rewind(ofp); else fseeko(ofp, -(off_t)sizeof(buf), SEEK_CUR); if (ferror(ofp)) errx(1, "%s: can't seek", currfile); if ((nread = fread(buf, 1, sizeof(buf), ofp)) == 0) errx(1, "can't read overflowed output"); if (fseeko(ofp, -(off_t)nread, SEEK_CUR) != 0) err(1, "%s", currfile); for (i = 1; i <= nread; i++) if (buf[nread - i] == '\n' && n-- == 0) break; if (ftello(ofp) == 0) break; } while (n > 0); if (fseeko(ofp, nread - i + 1, SEEK_CUR) != 0) err(1, "%s", currfile); /* * get_line() will read from here. Next call will truncate to * truncofs in this file. */ overfile = ofp; truncofs = ftello(overfile); } /* Handle splits for /regexp/ and %regexp% patterns. */ static void do_rexp(const char *expr) { regex_t cre; intmax_t nwritten; long ofs; int first; char *ecopy, *ep, *p, *pofs, *re; FILE *ofp; if ((ecopy = strdup(expr)) == NULL) err(1, "strdup"); re = ecopy + 1; if ((pofs = strrchr(ecopy, *expr)) == NULL || pofs[-1] == '\\') errx(1, "%s: missing trailing %c", expr, *expr); *pofs++ = '\0'; if (*pofs != '\0') { errno = 0; ofs = strtol(pofs, &ep, 10); if (*ep != '\0' || errno != 0) errx(1, "%s: bad offset", pofs); } else ofs = 0; if (regcomp(&cre, re, REG_BASIC|REG_NOSUB) != 0) errx(1, "%s: bad regular expression", re); if (*expr == '/') /* /regexp/: Save results to a file. */ ofp = newfile(); else { /* %regexp%: Make a temporary file for overflow. */ if ((ofp = tmpfile()) == NULL) err(1, "tmpfile"); } /* Read and output lines until we get a match. */ first = 1; while ((p = get_line()) != NULL) { if (fputs(p, ofp) == EOF) break; if (!first && regexec(&cre, p, 0, NULL, 0) == 0) break; first = 0; } if (p == NULL) { toomuch(NULL, 0); errx(1, "%s: no match", re); } if (ofs <= 0) { /* * Negative (or zero) offset: throw back any lines we should * not have read yet. */ if (p != NULL) { toomuch(ofp, -ofs + 1); nwritten = (intmax_t)truncofs; } else nwritten = (intmax_t)ftello(ofp); } else { /* * Positive offset: copy the requested number of lines * after the match. */ while (--ofs > 0 && (p = get_line()) != NULL) fputs(p, ofp); toomuch(NULL, 0); nwritten = (intmax_t)ftello(ofp); if (fclose(ofp) != 0) err(1, "%s", currfile); } if (!sflag && *expr == '/') printf("%jd\n", nwritten); regfree(&cre); free(ecopy); } /* Handle splits based on line number. */ static void do_lineno(const char *expr) { long lastline, tgtline; char *ep, *p; FILE *ofp; errno = 0; tgtline = strtol(expr, &ep, 10); if (tgtline <= 0 || errno != 0 || *ep != '\0') errx(1, "%s: bad line number", expr); lastline = tgtline; if (lastline <= lineno) errx(1, "%s: can't go backwards", expr); while (nfiles < maxfiles - 1) { ofp = newfile(); while (lineno + 1 != lastline) { if ((p = get_line()) == NULL) errx(1, "%ld: out of range", lastline); if (fputs(p, ofp) == EOF) break; } if (!sflag) printf("%jd\n", (intmax_t)ftello(ofp)); if (fclose(ofp) != 0) err(1, "%s", currfile); if (reps-- == 0) break; lastline += tgtline; } } diff --git a/usr.bin/ctags/C.c b/usr.bin/ctags/C.c index ba0ef146058b..283a08ec555d 100644 --- a/usr.bin/ctags/C.c +++ b/usr.bin/ctags/C.c @@ -1,560 +1,557 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1987, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include #include #include #include #include #include "ctags.h" static int func_entry(void); static void hash_entry(void); static void skip_string(int); static int str_entry(int); /* * c_entries -- * read .c and .h files and call appropriate routines */ void c_entries(void) { int c; /* current character */ int level; /* brace level */ int token; /* if reading a token */ int t_def; /* if reading a typedef */ int t_level; /* typedef's brace level */ char *sp; /* buffer pointer */ char tok[MAXTOKEN]; /* token buffer */ lineftell = ftell(inf); sp = tok; token = t_def = NO; t_level = -1; level = 0; lineno = 1; while (GETC(!=, EOF)) { switch (c) { /* * Here's where it DOESN'T handle: { * foo(a) * { * #ifdef notdef * } * #endif * if (a) * puts("hello, world"); * } */ case '{': ++level; goto endtok; case '}': /* * if level goes below zero, try and fix * it, even though we've already messed up */ if (--level < 0) level = 0; goto endtok; case '\n': SETLINE; /* * the above 3 cases are similar in that they * are special characters that also end tokens. */ endtok: if (sp > tok) { *sp = EOS; token = YES; sp = tok; } else token = NO; continue; /* * We ignore quoted strings and character constants * completely. */ case '"': case '\'': skip_string(c); break; /* * comments can be fun; note the state is unchanged after * return, in case we found: * "foo() XX comment XX { int bar; }" */ case '/': if (GETC(==, '*') || c == '/') { skip_comment(c); continue; } (void)ungetc(c, inf); c = '/'; goto storec; /* hash marks flag #define's. */ case '#': if (sp == tok) { hash_entry(); break; } goto storec; /* * if we have a current token, parenthesis on * level zero indicates a function. */ case '(': if (!level && token) { int curline; if (sp != tok) *sp = EOS; /* * grab the line immediately, we may * already be wrong, for example, * foo\n * (arg1, */ get_line(); curline = lineno; if (func_entry()) { ++level; pfnote(tok, curline); } break; } goto storec; /* * semi-colons indicate the end of a typedef; if we find a * typedef we search for the next semi-colon of the same * level as the typedef. Ignoring "structs", they are * tricky, since you can find: * * "typedef long time_t;" * "typedef unsigned int u_int;" * "typedef unsigned int u_int [10];" * * If looking at a typedef, we save a copy of the last token * found. Then, when we find the ';' we take the current * token if it starts with a valid token name, else we take * the one we saved. There's probably some reasonable * alternative to this... */ case ';': if (t_def && level == t_level) { t_def = NO; get_line(); if (sp != tok) *sp = EOS; pfnote(tok, lineno); break; } goto storec; /* * store characters until one that can't be part of a token * comes along; check the current token against certain * reserved words. */ default: /* ignore whitespace */ if (c == ' ' || c == '\t') { int save = c; while (GETC(!=, EOF) && (c == ' ' || c == '\t')) ; if (c == EOF) return; (void)ungetc(c, inf); c = save; } storec: if (!intoken(c)) { if (sp == tok) break; *sp = EOS; if (tflag) { /* no typedefs inside typedefs */ if (!t_def && !memcmp(tok, "typedef",8)) { t_def = YES; t_level = level; break; } /* catch "typedef struct" */ if ((!t_def || t_level < level) && (!memcmp(tok, "struct", 7) || !memcmp(tok, "union", 6) || !memcmp(tok, "enum", 5))) { /* * get line immediately; * may change before '{' */ get_line(); if (str_entry(c)) ++level; break; /* } */ } } sp = tok; } else if (sp != tok || begtoken(c)) { if (sp == tok + sizeof tok - 1) /* Too long -- truncate it */ *sp = EOS; else *sp++ = c; token = YES; } continue; } sp = tok; token = NO; } } /* * func_entry -- * handle a function reference */ static int func_entry(void) { int c; /* current character */ int level = 0; /* for matching '()' */ static char attribute[] = "__attribute__"; char maybe_attribute[sizeof attribute + 1], *anext; /* * Find the end of the assumed function declaration. * Note that ANSI C functions can have type definitions so keep * track of the parentheses nesting level. */ while (GETC(!=, EOF)) { switch (c) { case '\'': case '"': /* skip strings and character constants */ skip_string(c); break; case '/': /* skip comments */ if (GETC(==, '*') || c == '/') skip_comment(c); break; case '(': level++; break; case ')': if (level == 0) goto fnd; level--; break; case '\n': SETLINE; } } return (NO); fnd: /* * we assume that the character after a function's right paren * is a token character if it's a function and a non-token * character if it's a declaration. Comments don't count... */ for (anext = maybe_attribute;;) { while (GETC(!=, EOF) && iswhite(c)) if (c == '\n') SETLINE; if (c == EOF) return NO; /* * Recognize the gnu __attribute__ extension, which would * otherwise make the heuristic test DTWT */ if (anext == maybe_attribute) { if (intoken(c)) { *anext++ = c; continue; } } else { if (intoken(c)) { if (anext - maybe_attribute < (ptrdiff_t)(sizeof attribute - 1)) *anext++ = c; else break; continue; } else { *anext++ = '\0'; if (strcmp(maybe_attribute, attribute) == 0) { (void)ungetc(c, inf); return NO; } break; } } if (intoken(c) || c == '{') break; if (c == '/' && (GETC(==, '*') || c == '/')) skip_comment(c); else { /* don't ever "read" '/' */ (void)ungetc(c, inf); return (NO); } } if (c != '{') (void)skip_key('{'); return (YES); } /* * hash_entry -- * handle a line starting with a '#' */ static void hash_entry(void) { int c; /* character read */ int curline; /* line started on */ char *sp; /* buffer pointer */ char tok[MAXTOKEN]; /* storage buffer */ /* ignore leading whitespace */ while (GETC(!=, EOF) && (c == ' ' || c == '\t')) ; (void)ungetc(c, inf); curline = lineno; for (sp = tok;;) { /* get next token */ if (GETC(==, EOF)) return; if (iswhite(c)) break; if (sp == tok + sizeof tok - 1) /* Too long -- truncate it */ *sp = EOS; else *sp++ = c; } *sp = EOS; if (memcmp(tok, "define", 6)) /* only interested in #define's */ goto skip; for (;;) { /* this doesn't handle "#define \n" */ if (GETC(==, EOF)) return; if (!iswhite(c)) break; } for (sp = tok;;) { /* get next token */ if (sp == tok + sizeof tok - 1) /* Too long -- truncate it */ *sp = EOS; else *sp++ = c; if (GETC(==, EOF)) return; /* * this is where it DOESN'T handle * "#define \n" */ if (!intoken(c)) break; } *sp = EOS; if (dflag || c == '(') { /* only want macros */ get_line(); pfnote(tok, curline); } skip: if (c == '\n') { /* get rid of rest of define */ SETLINE if (*(sp - 1) != '\\') return; } (void)skip_key('\n'); } /* * str_entry -- * handle a struct, union or enum entry */ static int str_entry(int c) /* c is current character */ { int curline; /* line started on */ char *sp; /* buffer pointer */ char tok[LINE_MAX]; /* storage buffer */ curline = lineno; while (iswhite(c)) if (GETC(==, EOF)) return (NO); if (c == '{') /* it was "struct {" */ return (YES); for (sp = tok;;) { /* get next token */ if (sp == tok + sizeof tok - 1) /* Too long -- truncate it */ *sp = EOS; else *sp++ = c; if (GETC(==, EOF)) return (NO); if (!intoken(c)) break; } switch (c) { case '{': /* it was "struct foo{" */ --sp; break; case '\n': /* it was "struct foo\n" */ SETLINE; /*FALLTHROUGH*/ default: /* probably "struct foo " */ while (GETC(!=, EOF)) if (!iswhite(c)) break; if (c != '{') { (void)ungetc(c, inf); return (NO); } } *sp = EOS; pfnote(tok, curline); return (YES); } /* * skip_comment -- * skip over comment */ void skip_comment(int t) /* t is comment character */ { int c; /* character read */ int star; /* '*' flag */ for (star = 0; GETC(!=, EOF);) switch(c) { /* comments don't nest, nor can they be escaped. */ case '*': star = YES; break; case '/': if (star && t == '*') return; break; case '\n': SETLINE; if (t == '/') return; /*FALLTHROUGH*/ default: star = NO; break; } } /* * skip_string -- * skip to the end of a string or character constant. */ void skip_string(int key) { int c, skip; for (skip = NO; GETC(!=, EOF); ) switch (c) { case '\\': /* a backslash escapes anything */ skip = !skip; /* we toggle in case it's "\\" */ break; case '\n': SETLINE; /*FALLTHROUGH*/ default: if (c == key && !skip) return; skip = NO; } } /* * skip_key -- * skip to next char "key" */ int skip_key(int key) { int c, skip, retval; for (skip = retval = NO; GETC(!=, EOF);) switch(c) { case '\\': /* a backslash escapes anything */ skip = !skip; /* we toggle in case it's "\\" */ break; case ';': /* special case for yacc; if one */ case '|': /* of these chars occurs, we may */ retval = YES; /* have moved out of the rule */ break; /* not used by C */ case '\'': case '"': /* skip strings and character constants */ skip_string(c); break; case '/': /* skip comments */ if (GETC(==, '*') || c == '/') { skip_comment(c); break; } (void)ungetc(c, inf); c = '/'; goto norm; case '\n': SETLINE; /*FALLTHROUGH*/ default: norm: if (c == key && !skip) return (retval); skip = NO; } return (retval); } diff --git a/usr.bin/ctags/fortran.c b/usr.bin/ctags/fortran.c index 8c355cecd3ca..55d8d824f568 100644 --- a/usr.bin/ctags/fortran.c +++ b/usr.bin/ctags/fortran.c @@ -1,165 +1,162 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1987, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include #include #include #include #include #include "ctags.h" static void takeprec(void); char *lbp; /* line buffer pointer */ int PF_funcs(void) { bool pfcnt; /* pascal/fortran functions found */ char *cp; char tok[MAXTOKEN]; for (pfcnt = NO;;) { lineftell = ftell(inf); if (!fgets(lbuf, sizeof(lbuf), inf)) return (pfcnt); ++lineno; lbp = lbuf; if (*lbp == '%') /* Ratfor escape to fortran */ ++lbp; for (; isspace(*lbp); ++lbp) continue; if (!*lbp) continue; switch (*lbp | ' ') { /* convert to lower-case */ case 'c': if (cicmp("complex") || cicmp("character")) takeprec(); break; case 'd': if (cicmp("double")) { for (; isspace(*lbp); ++lbp) continue; if (!*lbp) continue; if (cicmp("precision")) break; continue; } break; case 'i': if (cicmp("integer")) takeprec(); break; case 'l': if (cicmp("logical")) takeprec(); break; case 'r': if (cicmp("real")) takeprec(); break; } for (; isspace(*lbp); ++lbp) continue; if (!*lbp) continue; switch (*lbp | ' ') { case 'f': if (cicmp("function")) break; continue; case 'p': if (cicmp("program") || cicmp("procedure")) break; continue; case 's': if (cicmp("subroutine")) break; default: continue; } for (; isspace(*lbp); ++lbp) continue; if (!*lbp) continue; for (cp = lbp + 1; *cp && intoken(*cp); ++cp) continue; if (cp == lbp + 1) continue; *cp = EOS; (void)strlcpy(tok, lbp, sizeof(tok)); /* possible trunc */ get_line(); /* process line for ex(1) */ pfnote(tok, lineno); pfcnt = YES; } /*NOTREACHED*/ } /* * cicmp -- * do case-independent strcmp */ int cicmp(const char *cp) { int len; char *bp; for (len = 0, bp = lbp; *cp && (*cp &~ ' ') == (*bp++ &~ ' '); ++cp, ++len) continue; if (!*cp) { lbp += len; return (YES); } return (NO); } static void takeprec(void) { for (; isspace(*lbp); ++lbp) continue; if (*lbp == '*') { for (++lbp; isspace(*lbp); ++lbp) continue; if (!isdigit(*lbp)) --lbp; /* force failure */ else while (isdigit(*++lbp)) continue; } } diff --git a/usr.bin/ctags/lisp.c b/usr.bin/ctags/lisp.c index e6e22fb71cfb..e219fcee4634 100644 --- a/usr.bin/ctags/lisp.c +++ b/usr.bin/ctags/lisp.c @@ -1,103 +1,100 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1987, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include #include #include #include #include #include "ctags.h" /* * lisp tag functions * just look for (def or (DEF */ void l_entries(void) { int special; char *cp; char savedc; char tok[MAXTOKEN]; for (;;) { lineftell = ftell(inf); if (!fgets(lbuf, sizeof(lbuf), inf)) return; ++lineno; lbp = lbuf; if (!cicmp("(def")) continue; special = NO; switch(*lbp | ' ') { case 'm': if (cicmp("method")) special = YES; break; case 'w': if (cicmp("wrapper") || cicmp("whopper")) special = YES; } for (; !isspace(*lbp); ++lbp) continue; for (; isspace(*lbp); ++lbp) continue; for (cp = lbp; *cp && *cp != '\n'; ++cp) continue; *cp = EOS; if (special) { if (!(cp = strchr(lbp, ')'))) continue; for (; cp >= lbp && *cp != ':'; --cp) continue; if (cp < lbp) continue; lbp = cp; for (; *cp && *cp != ')' && *cp != ' '; ++cp) continue; } else for (cp = lbp + 1; *cp && *cp != '(' && *cp != ' '; ++cp) continue; savedc = *cp; *cp = EOS; (void)strlcpy(tok, lbp, sizeof(tok)); /* possible trunc */ *cp = savedc; get_line(); pfnote(tok, lineno); } /*NOTREACHED*/ } diff --git a/usr.bin/ctags/print.c b/usr.bin/ctags/print.c index ea8a603a040a..e1b0957104c0 100644 --- a/usr.bin/ctags/print.c +++ b/usr.bin/ctags/print.c @@ -1,110 +1,107 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1987, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include #include #include #include #include "ctags.h" /* * get_line -- * get the line the token of interest occurred on, * prepare it for printing. */ void get_line(void) { long saveftell; int c; int cnt; char *cp; saveftell = ftell(inf); (void)fseek(inf, lineftell, L_SET); if (xflag) for (cp = lbuf; GETC(!=, EOF) && c != '\n'; *cp++ = c) continue; /* * do all processing here, so we don't step through the * line more than once; means you don't call this routine * unless you're sure you've got a keeper. */ else for (cnt = 0, cp = lbuf; GETC(!=, EOF) && cnt < ENDLINE; ++cnt) { if (c == '\\') { /* backslashes */ if (cnt > ENDLINE - 2) break; *cp++ = '\\'; *cp++ = '\\'; ++cnt; } else if (c == (int)searchar) { /* search character */ if (cnt > ENDLINE - 2) break; *cp++ = '\\'; *cp++ = c; ++cnt; } else if (c == '\n') { /* end of keep */ *cp++ = '$'; /* can find whole line */ break; } else *cp++ = c; } *cp = EOS; (void)fseek(inf, saveftell, L_SET); } /* * put_entries -- * write out the tags */ void put_entries(NODE *node) { if (node->left) put_entries(node->left); if (vflag) printf("%s %s %d\n", node->entry, node->file, (node->lno + 63) / 64); else if (xflag) printf("%-16s %4d %-16s %s\n", node->entry, node->lno, node->file, node->pat); else fprintf(outf, "%s\t%s\t%c^%s%c\n", node->entry, node->file, searchar, node->pat, searchar); if (node->right) put_entries(node->right); } diff --git a/usr.bin/ctags/tree.c b/usr.bin/ctags/tree.c index 3e654c3d909b..e8250bcbcf67 100644 --- a/usr.bin/ctags/tree.c +++ b/usr.bin/ctags/tree.c @@ -1,130 +1,127 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1987, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include #include #include #include #include #include #include "ctags.h" static void add_node(NODE *, NODE *); static void free_tree(NODE *); /* * pfnote -- * enter a new node in the tree */ void pfnote(const char *name, int ln) { NODE *np; char *fp; char nbuf[MAXTOKEN]; /*NOSTRICT*/ if (!(np = (NODE *)malloc(sizeof(NODE)))) { warnx("too many entries to sort"); put_entries(head); free_tree(head); /*NOSTRICT*/ if (!(head = np = (NODE *)malloc(sizeof(NODE)))) errx(1, "out of space"); } if (!xflag && !strcmp(name, "main")) { if (!(fp = strrchr(curfile, '/'))) fp = curfile; else ++fp; (void)snprintf(nbuf, sizeof(nbuf), "M%s", fp); fp = strrchr(nbuf, '.'); if (fp && !fp[2]) *fp = EOS; name = nbuf; } if (!(np->entry = strdup(name))) err(1, NULL); np->file = curfile; np->lno = ln; np->left = np->right = 0; if (!(np->pat = strdup(lbuf))) err(1, NULL); if (!head) head = np; else add_node(np, head); } static void add_node(NODE *node, NODE *cur_node) { int dif; dif = strcoll(node->entry, cur_node->entry); if (!dif) { if (node->file == cur_node->file) { if (!wflag) fprintf(stderr, "Duplicate entry in file %s, line %d: %s\nSecond entry ignored\n", node->file, lineno, node->entry); return; } if (!cur_node->been_warned) if (!wflag) fprintf(stderr, "Duplicate entry in files %s and %s: %s (Warning only)\n", node->file, cur_node->file, node->entry); cur_node->been_warned = YES; } else if (dif < 0) if (cur_node->left) add_node(node, cur_node->left); else cur_node->left = node; else if (cur_node->right) add_node(node, cur_node->right); else cur_node->right = node; } static void free_tree(NODE *node) { NODE *node_next; while (node) { if (node->right) free_tree(node->right); node_next = node->left; free(node); node = node_next; } } diff --git a/usr.bin/ctags/yacc.c b/usr.bin/ctags/yacc.c index 8044e4a2df60..502fbc98b714 100644 --- a/usr.bin/ctags/yacc.c +++ b/usr.bin/ctags/yacc.c @@ -1,148 +1,145 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1987, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include #include #include #include #include "ctags.h" /* * y_entries: * find the yacc tags and put them in. */ void y_entries(void) { int c; char *sp; bool in_rule; char tok[MAXTOKEN]; in_rule = NO; while (GETC(!=, EOF)) switch (c) { case '\n': SETLINE; /* FALLTHROUGH */ case ' ': case '\f': case '\r': case '\t': break; case '{': if (skip_key('}')) in_rule = NO; break; case '\'': case '"': if (skip_key(c)) in_rule = NO; break; case '%': if (GETC(==, '%')) return; (void)ungetc(c, inf); break; case '/': if (GETC(==, '*') || c == '/') skip_comment(c); else (void)ungetc(c, inf); break; case '|': case ';': in_rule = NO; break; default: if (in_rule || (!isalpha(c) && c != '.' && c != '_')) break; sp = tok; *sp++ = c; while (GETC(!=, EOF) && (intoken(c) || c == '.')) *sp++ = c; *sp = EOS; get_line(); /* may change before ':' */ while (iswhite(c)) { if (c == '\n') SETLINE; if (GETC(==, EOF)) return; } if (c == ':') { pfnote(tok, lineno); in_rule = YES; } else (void)ungetc(c, inf); } } /* * toss_yysec -- * throw away lines up to the next "\n%%\n" */ void toss_yysec(void) { int c; /* read character */ int state; /* * state == 0 : waiting * state == 1 : received a newline * state == 2 : received first % * state == 3 : received second % */ lineftell = ftell(inf); for (state = 0; GETC(!=, EOF);) switch (c) { case '\n': ++lineno; lineftell = ftell(inf); if (state == 3) /* done! */ return; state = 1; /* start over */ break; case '%': if (state) /* if 1 or 2 */ ++state; /* goto 3 */ break; default: state = 0; /* reset */ break; } } diff --git a/usr.bin/ctlstat/ctlstat.c b/usr.bin/ctlstat/ctlstat.c index 8aed0f52e350..3a2f9b7a0939 100644 --- a/usr.bin/ctlstat/ctlstat.c +++ b/usr.bin/ctlstat/ctlstat.c @@ -1,1012 +1,1011 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2004, 2008, 2009 Silicon Graphics International Corp. * Copyright (c) 2017 Alexander Motin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * $Id: //depot/users/kenm/FreeBSD-test2/usr.bin/ctlstat/ctlstat.c#4 $ */ /* * CAM Target Layer statistics program * * Authors: Ken Merry , Will Andrews */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The default amount of space we allocate for stats storage space. * We dynamically allocate more if needed. */ #define CTL_STAT_NUM_ITEMS 256 static int ctl_stat_bits; static const char *ctlstat_opts = "Cc:DPdhjl:n:p:tw:"; static const char *ctlstat_usage = "Usage: ctlstat [-CDPdjht] [-l lunnum]" "[-c count] [-n numdevs] [-w wait]\n"; struct ctl_cpu_stats { uint64_t user; uint64_t nice; uint64_t system; uint64_t intr; uint64_t idle; }; typedef enum { CTLSTAT_MODE_STANDARD, CTLSTAT_MODE_DUMP, CTLSTAT_MODE_JSON, CTLSTAT_MODE_PROMETHEUS, } ctlstat_mode_types; #define CTLSTAT_FLAG_CPU (1 << 0) #define CTLSTAT_FLAG_HEADER (1 << 1) #define CTLSTAT_FLAG_FIRST_RUN (1 << 2) #define CTLSTAT_FLAG_TOTALS (1 << 3) #define CTLSTAT_FLAG_DMA_TIME (1 << 4) #define CTLSTAT_FLAG_TIME_VALID (1 << 5) #define CTLSTAT_FLAG_MASK (1 << 6) #define CTLSTAT_FLAG_LUNS (1 << 7) #define CTLSTAT_FLAG_PORTS (1 << 8) #define F_CPU(ctx) ((ctx)->flags & CTLSTAT_FLAG_CPU) #define F_HDR(ctx) ((ctx)->flags & CTLSTAT_FLAG_HEADER) #define F_FIRST(ctx) ((ctx)->flags & CTLSTAT_FLAG_FIRST_RUN) #define F_TOTALS(ctx) ((ctx)->flags & CTLSTAT_FLAG_TOTALS) #define F_DMA(ctx) ((ctx)->flags & CTLSTAT_FLAG_DMA_TIME) #define F_TIMEVAL(ctx) ((ctx)->flags & CTLSTAT_FLAG_TIME_VALID) #define F_MASK(ctx) ((ctx)->flags & CTLSTAT_FLAG_MASK) #define F_LUNS(ctx) ((ctx)->flags & CTLSTAT_FLAG_LUNS) #define F_PORTS(ctx) ((ctx)->flags & CTLSTAT_FLAG_PORTS) struct ctlstat_context { ctlstat_mode_types mode; int flags; struct ctl_io_stats *cur_stats, *prev_stats; struct ctl_io_stats cur_total_stats[3], prev_total_stats[3]; struct timespec cur_time, prev_time; struct ctl_cpu_stats cur_cpu, prev_cpu; uint64_t cur_total_jiffies, prev_total_jiffies; uint64_t cur_idle, prev_idle; bitstr_t *item_mask; int cur_items, prev_items; int cur_alloc, prev_alloc; int numdevs; int header_interval; }; struct cctl_portlist_data { int level; struct sbuf *cur_sb[32]; int id; int lun; int ntargets; char *target; char **targets; }; #ifndef min #define min(x,y) (((x) < (y)) ? (x) : (y)) #endif static void usage(int error); static int getstats(int fd, int *alloc_items, int *num_items, struct ctl_io_stats **xstats, struct timespec *cur_time, int *time_valid, bool ports); static int getcpu(struct ctl_cpu_stats *cpu_stats); static void compute_stats(struct ctl_io_stats *cur_stats, struct ctl_io_stats *prev_stats, long double etime, long double *mbsec, long double *kb_per_transfer, long double *transfers_per_second, long double *ms_per_transfer, long double *ms_per_dma, long double *dmas_per_second); static void usage(int error) { fputs(ctlstat_usage, error ? stderr : stdout); } static int getstats(int fd, int *alloc_items, int *num_items, struct ctl_io_stats **stats, struct timespec *cur_time, int *flags, bool ports) { struct ctl_get_io_stats get_stats; int more_space_count = 0; if (*alloc_items == 0) *alloc_items = CTL_STAT_NUM_ITEMS; retry: if (*stats == NULL) *stats = malloc(sizeof(**stats) * *alloc_items); memset(&get_stats, 0, sizeof(get_stats)); get_stats.alloc_len = *alloc_items * sizeof(**stats); memset(*stats, 0, get_stats.alloc_len); get_stats.stats = *stats; if (ioctl(fd, ports ? CTL_GET_PORT_STATS : CTL_GET_LUN_STATS, &get_stats) == -1) err(1, "CTL_GET_*_STATS ioctl returned error"); switch (get_stats.status) { case CTL_SS_OK: break; case CTL_SS_ERROR: err(1, "CTL_GET_*_STATS ioctl returned CTL_SS_ERROR"); break; case CTL_SS_NEED_MORE_SPACE: if (more_space_count >= 2) errx(1, "CTL_GET_*_STATS returned NEED_MORE_SPACE again"); *alloc_items = get_stats.num_items * 5 / 4; free(*stats); *stats = NULL; more_space_count++; goto retry; break; /* NOTREACHED */ default: errx(1, "CTL_GET_*_STATS ioctl returned unknown status %d", get_stats.status); break; } *num_items = get_stats.fill_len / sizeof(**stats); cur_time->tv_sec = get_stats.timestamp.tv_sec; cur_time->tv_nsec = get_stats.timestamp.tv_nsec; if (get_stats.flags & CTL_STATS_FLAG_TIME_VALID) *flags |= CTLSTAT_FLAG_TIME_VALID; else *flags &= ~CTLSTAT_FLAG_TIME_VALID; return (0); } static int getcpu(struct ctl_cpu_stats *cpu_stats) { long cp_time[CPUSTATES]; size_t cplen; cplen = sizeof(cp_time); if (sysctlbyname("kern.cp_time", &cp_time, &cplen, NULL, 0) == -1) { warn("sysctlbyname(kern.cp_time...) failed"); return (1); } cpu_stats->user = cp_time[CP_USER]; cpu_stats->nice = cp_time[CP_NICE]; cpu_stats->system = cp_time[CP_SYS]; cpu_stats->intr = cp_time[CP_INTR]; cpu_stats->idle = cp_time[CP_IDLE]; return (0); } static void compute_stats(struct ctl_io_stats *cur_stats, struct ctl_io_stats *prev_stats, long double etime, long double *mbsec, long double *kb_per_transfer, long double *transfers_per_second, long double *ms_per_transfer, long double *ms_per_dma, long double *dmas_per_second) { uint64_t total_bytes = 0, total_operations = 0, total_dmas = 0; struct bintime total_time_bt, total_dma_bt; struct timespec total_time_ts, total_dma_ts; int i; bzero(&total_time_bt, sizeof(total_time_bt)); bzero(&total_dma_bt, sizeof(total_dma_bt)); bzero(&total_time_ts, sizeof(total_time_ts)); bzero(&total_dma_ts, sizeof(total_dma_ts)); for (i = 0; i < CTL_STATS_NUM_TYPES; i++) { total_bytes += cur_stats->bytes[i]; total_operations += cur_stats->operations[i]; total_dmas += cur_stats->dmas[i]; bintime_add(&total_time_bt, &cur_stats->time[i]); bintime_add(&total_dma_bt, &cur_stats->dma_time[i]); if (prev_stats != NULL) { total_bytes -= prev_stats->bytes[i]; total_operations -= prev_stats->operations[i]; total_dmas -= prev_stats->dmas[i]; bintime_sub(&total_time_bt, &prev_stats->time[i]); bintime_sub(&total_dma_bt, &prev_stats->dma_time[i]); } } *mbsec = total_bytes; *mbsec /= 1024 * 1024; if (etime > 0.0) *mbsec /= etime; else *mbsec = 0; *kb_per_transfer = total_bytes; *kb_per_transfer /= 1024; if (total_operations > 0) *kb_per_transfer /= total_operations; else *kb_per_transfer = 0; *transfers_per_second = total_operations; *dmas_per_second = total_dmas; if (etime > 0.0) { *transfers_per_second /= etime; *dmas_per_second /= etime; } else { *transfers_per_second = 0; *dmas_per_second = 0; } bintime2timespec(&total_time_bt, &total_time_ts); bintime2timespec(&total_dma_bt, &total_dma_ts); if (total_operations > 0) { /* * Convert the timespec to milliseconds. */ *ms_per_transfer = total_time_ts.tv_sec * 1000; *ms_per_transfer += total_time_ts.tv_nsec / 1000000; *ms_per_transfer /= total_operations; } else *ms_per_transfer = 0; if (total_dmas > 0) { /* * Convert the timespec to milliseconds. */ *ms_per_dma = total_dma_ts.tv_sec * 1000; *ms_per_dma += total_dma_ts.tv_nsec / 1000000; *ms_per_dma /= total_dmas; } else *ms_per_dma = 0; } /* The dump_stats() and json_stats() functions perform essentially the same * purpose, but dump the statistics in different formats. JSON is more * conducive to programming, however. */ #define PRINT_BINTIME(bt) \ printf("%jd.%06ju", (intmax_t)(bt).sec, \ (uintmax_t)(((bt).frac >> 32) * 1000000 >> 32)) static const char *iotypes[] = {"NO IO", "READ", "WRITE"}; static void ctlstat_dump(struct ctlstat_context *ctx) { int iotype, i, n; struct ctl_io_stats *stats = ctx->cur_stats; for (i = n = 0; i < ctx->cur_items;i++) { if (F_MASK(ctx) && bit_test(ctx->item_mask, (int)stats[i].item) == 0) continue; printf("%s %d\n", F_PORTS(ctx) ? "port" : "lun", stats[i].item); for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) { printf(" io type %d (%s)\n", iotype, iotypes[iotype]); printf(" bytes %ju\n", (uintmax_t) stats[i].bytes[iotype]); printf(" operations %ju\n", (uintmax_t) stats[i].operations[iotype]); printf(" dmas %ju\n", (uintmax_t) stats[i].dmas[iotype]); printf(" io time "); PRINT_BINTIME(stats[i].time[iotype]); printf("\n dma time "); PRINT_BINTIME(stats[i].dma_time[iotype]); printf("\n"); } if (++n >= ctx->numdevs) break; } } static void ctlstat_json(struct ctlstat_context *ctx) { int iotype, i, n; struct ctl_io_stats *stats = ctx->cur_stats; printf("{\"%s\":[", F_PORTS(ctx) ? "ports" : "luns"); for (i = n = 0; i < ctx->cur_items; i++) { if (F_MASK(ctx) && bit_test(ctx->item_mask, (int)stats[i].item) == 0) continue; printf("{\"num\":%d,\"io\":[", stats[i].item); for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) { printf("{\"type\":\"%s\",", iotypes[iotype]); printf("\"bytes\":%ju,", (uintmax_t) stats[i].bytes[iotype]); printf("\"operations\":%ju,", (uintmax_t) stats[i].operations[iotype]); printf("\"dmas\":%ju,", (uintmax_t) stats[i].dmas[iotype]); printf("\"io time\":"); PRINT_BINTIME(stats[i].time[iotype]); printf(",\"dma time\":"); PRINT_BINTIME(stats[i].dma_time[iotype]); printf("}"); if (iotype < (CTL_STATS_NUM_TYPES - 1)) printf(","); /* continue io array */ } printf("]}"); if (++n >= ctx->numdevs) break; if (i < (ctx->cur_items - 1)) printf(","); /* continue lun array */ } printf("]}"); } #define CTLSTAT_PROMETHEUS_LOOP(field, collector) \ for (i = n = 0; i < ctx->cur_items; i++) { \ if (F_MASK(ctx) && bit_test(ctx->item_mask, \ (int)stats[i].item) == 0) \ continue; \ for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) { \ int idx = stats[i].item; \ /* \ * Note that Prometheus considers a label value of "" \ * to be the same as no label at all \ */ \ const char *target = ""; \ if (strcmp(collector, "port") == 0 && \ targdata.targets[idx] != NULL) \ { \ target = targdata.targets[idx]; \ } \ printf("iscsi_%s_" #field "{" \ "%s=\"%u\",target=\"%s\",type=\"%s\"} %" PRIu64 \ "\n", \ collector, collector, \ idx, target, iotypes[iotype], \ stats[i].field[iotype]); \ } \ } \ #define CTLSTAT_PROMETHEUS_TIMELOOP(field, collector) \ for (i = n = 0; i < ctx->cur_items; i++) { \ if (F_MASK(ctx) && bit_test(ctx->item_mask, \ (int)stats[i].item) == 0) \ continue; \ for (iotype = 0; iotype < CTL_STATS_NUM_TYPES; iotype++) { \ uint64_t us; \ struct timespec ts; \ int idx = stats[i].item; \ /* \ * Note that Prometheus considers a label value of "" \ * to be the same as no label at all \ */ \ const char *target = ""; \ if (strcmp(collector, "port") == 0 && \ targdata.targets[idx] != NULL) \ { \ target = targdata.targets[idx]; \ } \ bintime2timespec(&stats[i].field[iotype], &ts); \ us = ts.tv_sec * 1000000 + ts.tv_nsec / 1000; \ printf("iscsi_%s_" #field "{" \ "%s=\"%u\",target=\"%s\",type=\"%s\"} %" PRIu64 \ "\n", \ collector, collector, \ idx, target, iotypes[iotype], us); \ } \ } \ static void cctl_start_pelement(void *user_data, const char *name, const char **attr) { struct cctl_portlist_data* targdata = user_data; targdata->level++; if ((u_int)targdata->level >= (sizeof(targdata->cur_sb) / sizeof(targdata->cur_sb[0]))) errx(1, "%s: too many nesting levels, %zd max", __func__, sizeof(targdata->cur_sb) / sizeof(targdata->cur_sb[0])); targdata->cur_sb[targdata->level] = sbuf_new_auto(); if (targdata->cur_sb[targdata->level] == NULL) err(1, "%s: Unable to allocate sbuf", __func__); if (strcmp(name, "targ_port") == 0) { int i = 0; targdata->lun = -1; targdata->id = -1; free(targdata->target); targdata->target = NULL; while (attr[i]) { if (strcmp(attr[i], "id") == 0) { /* * Well-formed XML always pairs keys with * values in attr */ assert(attr[i + 1]); targdata->id = atoi(attr[i + 1]); } i += 2; } } } static void cctl_char_phandler(void *user_data, const XML_Char *str, int len) { struct cctl_portlist_data *targdata = user_data; sbuf_bcat(targdata->cur_sb[targdata->level], str, len); } static void cctl_end_pelement(void *user_data, const char *name) { struct cctl_portlist_data* targdata = user_data; char *str; if (targdata->cur_sb[targdata->level] == NULL) errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, targdata->level, name); if (sbuf_finish(targdata->cur_sb[targdata->level]) != 0) err(1, "%s: sbuf_finish", __func__); str = strdup(sbuf_data(targdata->cur_sb[targdata->level])); if (str == NULL) err(1, "%s can't allocate %zd bytes for string", __func__, sbuf_len(targdata->cur_sb[targdata->level])); sbuf_delete(targdata->cur_sb[targdata->level]); targdata->cur_sb[targdata->level] = NULL; targdata->level--; if (strcmp(name, "target") == 0) { free(targdata->target); targdata->target = str; } else if (strcmp(name, "targ_port") == 0) { if (targdata->id >= 0 && targdata->target != NULL) { if (targdata->id >= targdata->ntargets) { /* * This can happen for example if there are * targets with no LUNs. */ targdata->ntargets = MAX(targdata->ntargets * 2, targdata->id + 1); size_t newsize = targdata->ntargets * sizeof(char*); targdata->targets = rallocx(targdata->targets, newsize, MALLOCX_ZERO); } free(targdata->targets[targdata->id]); targdata->targets[targdata->id] = targdata->target; targdata->target = NULL; } free(str); } else { free(str); } } static void ctlstat_prometheus(int fd, struct ctlstat_context *ctx, bool ports) { struct ctl_io_stats *stats = ctx->cur_stats; struct ctl_lun_list list; struct cctl_portlist_data targdata; XML_Parser parser; char *port_str = NULL; int iotype, i, n, retval; int port_len = 4096; const char *collector; bzero(&targdata, sizeof(targdata)); targdata.ntargets = ctx->cur_items; targdata.targets = calloc(targdata.ntargets, sizeof(char*)); retry: port_str = (char *)realloc(port_str, port_len); bzero(&list, sizeof(list)); list.alloc_len = port_len; list.status = CTL_LUN_LIST_NONE; list.lun_xml = port_str; if (ioctl(fd, CTL_PORT_LIST, &list) == -1) err(1, "%s: error issuing CTL_PORT_LIST ioctl", __func__); if (list.status == CTL_LUN_LIST_ERROR) { warnx("%s: error returned from CTL_PORT_LIST ioctl:\n%s", __func__, list.error_str); } else if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) { port_len <<= 1; goto retry; } parser = XML_ParserCreate(NULL); if (parser == NULL) err(1, "%s: Unable to create XML parser", __func__); XML_SetUserData(parser, &targdata); XML_SetElementHandler(parser, cctl_start_pelement, cctl_end_pelement); XML_SetCharacterDataHandler(parser, cctl_char_phandler); retval = XML_Parse(parser, port_str, strlen(port_str), 1); if (retval != 1) { errx(1, "%s: Unable to parse XML: Error %d", __func__, XML_GetErrorCode(parser)); } XML_ParserFree(parser); collector = ports ? "port" : "lun"; printf("# HELP iscsi_%s_bytes Number of bytes\n" "# TYPE iscsi_%s_bytes counter\n", collector, collector); CTLSTAT_PROMETHEUS_LOOP(bytes, collector); printf("# HELP iscsi_%s_dmas Number of DMA\n" "# TYPE iscsi_%s_dmas counter\n", collector, collector); CTLSTAT_PROMETHEUS_LOOP(dmas, collector); printf("# HELP iscsi_%s_operations Number of operations\n" "# TYPE iscsi_%s_operations counter\n", collector, collector); CTLSTAT_PROMETHEUS_LOOP(operations, collector); printf("# HELP iscsi_%s_time Cumulative operation time in us\n" "# TYPE iscsi_%s_time counter\n", collector, collector); CTLSTAT_PROMETHEUS_TIMELOOP(time, collector); printf("# HELP iscsi_%s_dma_time Cumulative DMA time in us\n" "# TYPE iscsi_%s_dma_time counter\n", collector, collector); CTLSTAT_PROMETHEUS_TIMELOOP(dma_time, collector); for (i = 0; i < targdata.ntargets; i++) free(targdata.targets[i]); free(targdata.target); free(targdata.targets); fflush(stdout); } static void ctlstat_standard(struct ctlstat_context *ctx) { long double etime; uint64_t delta_jiffies, delta_idle; long double cpu_percentage; int i, j, n; cpu_percentage = 0; if (F_CPU(ctx) && (getcpu(&ctx->cur_cpu) != 0)) errx(1, "error returned from getcpu()"); etime = ctx->cur_time.tv_sec - ctx->prev_time.tv_sec + (ctx->prev_time.tv_nsec - ctx->cur_time.tv_nsec) * 1e-9; if (F_CPU(ctx)) { ctx->prev_total_jiffies = ctx->cur_total_jiffies; ctx->cur_total_jiffies = ctx->cur_cpu.user + ctx->cur_cpu.nice + ctx->cur_cpu.system + ctx->cur_cpu.intr + ctx->cur_cpu.idle; delta_jiffies = ctx->cur_total_jiffies; if (F_FIRST(ctx) == 0) delta_jiffies -= ctx->prev_total_jiffies; ctx->prev_idle = ctx->cur_idle; ctx->cur_idle = ctx->cur_cpu.idle; delta_idle = ctx->cur_idle - ctx->prev_idle; cpu_percentage = delta_jiffies - delta_idle; cpu_percentage /= delta_jiffies; cpu_percentage *= 100; } if (F_HDR(ctx)) { ctx->header_interval--; if (ctx->header_interval <= 0) { if (F_CPU(ctx)) fprintf(stdout, " CPU"); if (F_TOTALS(ctx)) { fprintf(stdout, "%s Read %s" " Write %s Total\n", (F_TIMEVAL(ctx) != 0) ? " " : "", (F_TIMEVAL(ctx) != 0) ? " " : "", (F_TIMEVAL(ctx) != 0) ? " " : ""); n = 3; } else { for (i = n = 0; i < min(ctl_stat_bits, ctx->cur_items); i++) { int item; /* * Obviously this won't work with * LUN numbers greater than a signed * integer. */ item = (int)ctx->cur_stats[i].item; if (F_MASK(ctx) && bit_test(ctx->item_mask, item) == 0) continue; fprintf(stdout, "%15.6s%d %s", F_PORTS(ctx) ? "port" : "lun", item, (F_TIMEVAL(ctx) != 0) ? " " : ""); if (++n >= ctx->numdevs) break; } fprintf(stdout, "\n"); } if (F_CPU(ctx)) fprintf(stdout, " "); for (i = 0; i < n; i++) fprintf(stdout, "%s KB/t %s MB/s", (F_TIMEVAL(ctx) != 0) ? " ms" : "", (F_DMA(ctx) == 0) ? "tps" : "dps"); fprintf(stdout, "\n"); ctx->header_interval = 20; } } if (F_CPU(ctx)) fprintf(stdout, "%3.0Lf%%", cpu_percentage); if (F_TOTALS(ctx) != 0) { long double mbsec[3]; long double kb_per_transfer[3]; long double transfers_per_sec[3]; long double ms_per_transfer[3]; long double ms_per_dma[3]; long double dmas_per_sec[3]; for (i = 0; i < 3; i++) ctx->prev_total_stats[i] = ctx->cur_total_stats[i]; memset(&ctx->cur_total_stats, 0, sizeof(ctx->cur_total_stats)); /* Use macros to make the next loop more readable. */ #define ADD_STATS_BYTES(st, i, j) \ ctx->cur_total_stats[st].bytes[j] += \ ctx->cur_stats[i].bytes[j] #define ADD_STATS_OPERATIONS(st, i, j) \ ctx->cur_total_stats[st].operations[j] += \ ctx->cur_stats[i].operations[j] #define ADD_STATS_DMAS(st, i, j) \ ctx->cur_total_stats[st].dmas[j] += \ ctx->cur_stats[i].dmas[j] #define ADD_STATS_TIME(st, i, j) \ bintime_add(&ctx->cur_total_stats[st].time[j], \ &ctx->cur_stats[i].time[j]) #define ADD_STATS_DMA_TIME(st, i, j) \ bintime_add(&ctx->cur_total_stats[st].dma_time[j], \ &ctx->cur_stats[i].dma_time[j]) for (i = 0; i < ctx->cur_items; i++) { if (F_MASK(ctx) && bit_test(ctx->item_mask, (int)ctx->cur_stats[i].item) == 0) continue; for (j = 0; j < CTL_STATS_NUM_TYPES; j++) { ADD_STATS_BYTES(2, i, j); ADD_STATS_OPERATIONS(2, i, j); ADD_STATS_DMAS(2, i, j); ADD_STATS_TIME(2, i, j); ADD_STATS_DMA_TIME(2, i, j); } ADD_STATS_BYTES(0, i, CTL_STATS_READ); ADD_STATS_OPERATIONS(0, i, CTL_STATS_READ); ADD_STATS_DMAS(0, i, CTL_STATS_READ); ADD_STATS_TIME(0, i, CTL_STATS_READ); ADD_STATS_DMA_TIME(0, i, CTL_STATS_READ); ADD_STATS_BYTES(1, i, CTL_STATS_WRITE); ADD_STATS_OPERATIONS(1, i, CTL_STATS_WRITE); ADD_STATS_DMAS(1, i, CTL_STATS_WRITE); ADD_STATS_TIME(1, i, CTL_STATS_WRITE); ADD_STATS_DMA_TIME(1, i, CTL_STATS_WRITE); } for (i = 0; i < 3; i++) { compute_stats(&ctx->cur_total_stats[i], F_FIRST(ctx) ? NULL : &ctx->prev_total_stats[i], etime, &mbsec[i], &kb_per_transfer[i], &transfers_per_sec[i], &ms_per_transfer[i], &ms_per_dma[i], &dmas_per_sec[i]); if (F_DMA(ctx) != 0) fprintf(stdout, " %5.1Lf", ms_per_dma[i]); else if (F_TIMEVAL(ctx) != 0) fprintf(stdout, " %5.1Lf", ms_per_transfer[i]); fprintf(stdout, " %4.0Lf %5.0Lf %4.0Lf", kb_per_transfer[i], (F_DMA(ctx) == 0) ? transfers_per_sec[i] : dmas_per_sec[i], mbsec[i]); } } else { for (i = n = 0; i < min(ctl_stat_bits, ctx->cur_items); i++) { long double mbsec, kb_per_transfer; long double transfers_per_sec; long double ms_per_transfer; long double ms_per_dma; long double dmas_per_sec; if (F_MASK(ctx) && bit_test(ctx->item_mask, (int)ctx->cur_stats[i].item) == 0) continue; for (j = 0; j < ctx->prev_items; j++) { if (ctx->prev_stats[j].item == ctx->cur_stats[i].item) break; } if (j >= ctx->prev_items) j = -1; compute_stats(&ctx->cur_stats[i], j >= 0 ? &ctx->prev_stats[j] : NULL, etime, &mbsec, &kb_per_transfer, &transfers_per_sec, &ms_per_transfer, &ms_per_dma, &dmas_per_sec); if (F_DMA(ctx)) fprintf(stdout, " %5.1Lf", ms_per_dma); else if (F_TIMEVAL(ctx) != 0) fprintf(stdout, " %5.1Lf", ms_per_transfer); fprintf(stdout, " %4.0Lf %5.0Lf %4.0Lf", kb_per_transfer, (F_DMA(ctx) == 0) ? transfers_per_sec : dmas_per_sec, mbsec); if (++n >= ctx->numdevs) break; } } } static void get_and_print_stats(int fd, struct ctlstat_context *ctx, bool ports) { struct ctl_io_stats *tmp_stats; int c; tmp_stats = ctx->prev_stats; ctx->prev_stats = ctx->cur_stats; ctx->cur_stats = tmp_stats; c = ctx->prev_alloc; ctx->prev_alloc = ctx->cur_alloc; ctx->cur_alloc = c; c = ctx->prev_items; ctx->prev_items = ctx->cur_items; ctx->cur_items = c; ctx->prev_time = ctx->cur_time; ctx->prev_cpu = ctx->cur_cpu; if (getstats(fd, &ctx->cur_alloc, &ctx->cur_items, &ctx->cur_stats, &ctx->cur_time, &ctx->flags, ports) != 0) errx(1, "error returned from getstats()"); switch(ctx->mode) { case CTLSTAT_MODE_STANDARD: ctlstat_standard(ctx); break; case CTLSTAT_MODE_DUMP: ctlstat_dump(ctx); break; case CTLSTAT_MODE_JSON: ctlstat_json(ctx); break; case CTLSTAT_MODE_PROMETHEUS: ctlstat_prometheus(fd, ctx, ports); break; default: break; } } int main(int argc, char **argv) { int c; int count, waittime; int fd, retval; size_t size; struct ctlstat_context ctx; /* default values */ retval = 0; waittime = 1; count = -1; memset(&ctx, 0, sizeof(ctx)); ctx.numdevs = 3; ctx.mode = CTLSTAT_MODE_STANDARD; ctx.flags |= CTLSTAT_FLAG_CPU; ctx.flags |= CTLSTAT_FLAG_FIRST_RUN; ctx.flags |= CTLSTAT_FLAG_HEADER; size = sizeof(ctl_stat_bits); if (sysctlbyname("kern.cam.ctl.max_luns", &ctl_stat_bits, &size, NULL, 0) == -1) { /* Backward compatibility for where the sysctl wasn't exposed */ ctl_stat_bits = 1024; } ctx.item_mask = bit_alloc(ctl_stat_bits); if (ctx.item_mask == NULL) err(1, "bit_alloc() failed"); while ((c = getopt(argc, argv, ctlstat_opts)) != -1) { switch (c) { case 'C': ctx.flags &= ~CTLSTAT_FLAG_CPU; break; case 'c': count = atoi(optarg); break; case 'd': ctx.flags |= CTLSTAT_FLAG_DMA_TIME; break; case 'D': ctx.mode = CTLSTAT_MODE_DUMP; waittime = 30; break; case 'h': ctx.flags &= ~CTLSTAT_FLAG_HEADER; break; case 'j': ctx.mode = CTLSTAT_MODE_JSON; waittime = 30; break; case 'l': { int cur_lun; cur_lun = atoi(optarg); if (cur_lun > ctl_stat_bits) errx(1, "Invalid LUN number %d", cur_lun); if (!F_MASK(&ctx)) ctx.numdevs = 1; else ctx.numdevs++; bit_set(ctx.item_mask, cur_lun); ctx.flags |= CTLSTAT_FLAG_MASK; ctx.flags |= CTLSTAT_FLAG_LUNS; break; } case 'n': ctx.numdevs = atoi(optarg); break; case 'p': { int cur_port; cur_port = atoi(optarg); if (cur_port > ctl_stat_bits) errx(1, "Invalid port number %d", cur_port); if (!F_MASK(&ctx)) ctx.numdevs = 1; else ctx.numdevs++; bit_set(ctx.item_mask, cur_port); ctx.flags |= CTLSTAT_FLAG_MASK; ctx.flags |= CTLSTAT_FLAG_PORTS; break; } case 'P': ctx.mode = CTLSTAT_MODE_PROMETHEUS; break; case 't': ctx.flags |= CTLSTAT_FLAG_TOTALS; break; case 'w': waittime = atoi(optarg); break; default: retval = 1; usage(retval); exit(retval); break; } } if (F_LUNS(&ctx) && F_PORTS(&ctx)) errx(1, "Options -p and -l are exclusive."); if (ctx.mode == CTLSTAT_MODE_PROMETHEUS) { if ((count != -1) || (waittime != 1) || (F_PORTS(&ctx)) || /* NB: -P could be compatible with -t in the future */ (ctx.flags & CTLSTAT_FLAG_TOTALS)) { errx(1, "Option -P is exclusive with -p, -c, -w, and -t"); } count = 1; } if (!F_LUNS(&ctx) && !F_PORTS(&ctx)) { if (F_TOTALS(&ctx)) ctx.flags |= CTLSTAT_FLAG_PORTS; else ctx.flags |= CTLSTAT_FLAG_LUNS; } if ((fd = open(CTL_DEFAULT_DEV, O_RDWR)) == -1) err(1, "cannot open %s", CTL_DEFAULT_DEV); if (ctx.mode == CTLSTAT_MODE_PROMETHEUS) { /* * NB: Some clients will print a warning if we don't set * Content-Length, but they still work. And the data still * gets into Prometheus. */ printf("HTTP/1.1 200 OK\r\n" "Connection: close\r\n" "Content-Type: text/plain; version=0.0.4\r\n" "\r\n"); } for (;count != 0;) { bool ports; if (ctx.mode == CTLSTAT_MODE_PROMETHEUS) { get_and_print_stats(fd, &ctx, false); get_and_print_stats(fd, &ctx, true); } else { ports = ctx.flags & CTLSTAT_FLAG_PORTS; get_and_print_stats(fd, &ctx, ports); } fprintf(stdout, "\n"); fflush(stdout); ctx.flags &= ~CTLSTAT_FLAG_FIRST_RUN; if (count != 1) sleep(waittime); if (count > 0) count--; } exit (retval); } /* * vim: ts=8 */ diff --git a/usr.bin/diff/diffreg.c b/usr.bin/diff/diffreg.c index 447b48fe79ed..348741f8ccca 100644 --- a/usr.bin/diff/diffreg.c +++ b/usr.bin/diff/diffreg.c @@ -1,1683 +1,1682 @@ /* $OpenBSD: diffreg.c,v 1.93 2019/06/28 13:35:00 deraadt Exp $ */ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) Caldera International Inc. 2001-2002. * 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 and documentation 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 or owned by Caldera * International, Inc. * 4. Neither the name of Caldera International, Inc. nor the names of other * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pr.h" #include "diff.h" #include "xmalloc.h" /* * diff - compare two files. */ /* * Uses an algorithm due to Harold Stone, which finds a pair of longest * identical subsequences in the two files. * * The major goal is to generate the match vector J. J[i] is the index of * the line in file1 corresponding to line i file0. J[i] = 0 if there is no * such line in file1. * * Lines are hashed so as to work in core. All potential matches are * located by sorting the lines of each file on the hash (called * ``value''). In particular, this collects the equivalence classes in * file1 together. Subroutine equiv replaces the value of each line in * file0 by the index of the first element of its matching equivalence in * (the reordered) file1. To save space equiv squeezes file1 into a single * array member in which the equivalence classes are simply concatenated, * except that their first members are flagged by changing sign. * * Next the indices that point into member are unsorted into array class * according to the original order of file0. * * The cleverness lies in routine stone. This marches through the lines of * file0, developing a vector klist of "k-candidates". At step i * a k-candidate is a matched pair of lines x,y (x in file0 y in file1) * such that there is a common subsequence of length k between the first * i lines of file0 and the first y lines of file1, but there is no such * subsequence for any smaller y. x is the earliest possible mate to y that * occurs in such a subsequence. * * Whenever any of the members of the equivalence class of lines in file1 * matable to a line in file0 has serial number less than the y of some * k-candidate, that k-candidate with the smallest such y is replaced. The * new k-candidate is chained (via pred) to the current k-1 candidate so * that the actual subsequence can be recovered. When a member has serial * number greater that the y of all k-candidates, the klist is extended. At * the end, the longest subsequence is pulled out and placed in the array J * by unravel. * * With J in hand, the matches there recorded are check'ed against reality * to assure that no spurious matches have crept in due to hashing. If they * have, they are broken, and "jackpot" is recorded -- a harmless matter * except that a true match for a spuriously mated line may now be * unnecessarily reported as a change. * * Much of the complexity of the program comes simply from trying to * minimize core utilization and maximize the range of doable problems by * dynamically allocating what is needed and reusing what is not. The core * requirements for problems larger than somewhat are (in words) * 2*length(file0) + length(file1) + 3*(number of k-candidates installed), * typically about 6n words for files of length n. */ struct cand { int x; int y; int pred; }; static struct line { int serial; int value; } *file[2]; /* * The following struct is used to record change information when * doing a "context" or "unified" diff. (see routine "change" to * understand the highly mnemonic field names) */ struct context_vec { int a; /* start line in old file */ int b; /* end line in old file */ int c; /* start line in new file */ int d; /* end line in new file */ }; enum readhash { RH_BINARY, RH_OK, RH_EOF }; #define MIN_PAD 1 static FILE *opentemp(const char *); static void output(char *, FILE *, char *, FILE *, int); static void check(FILE *, FILE *, int); static void range(int, int, const char *); static void uni_range(int, int); static void dump_context_vec(FILE *, FILE *, int); static void dump_unified_vec(FILE *, FILE *, int); static bool prepare(int, FILE *, size_t, int); static void prune(void); static void equiv(struct line *, int, struct line *, int, int *); static void unravel(int); static void unsort(struct line *, int, int *); static void change(char *, FILE *, char *, FILE *, int, int, int, int, int *); static void sort(struct line *, int); static void print_header(const char *, const char *); static void print_space(int, int, int); static bool ignoreline_pattern(char *); static bool ignoreline(char *, bool); static int asciifile(FILE *); static int fetch(long *, int, int, FILE *, int, int, int); static int newcand(int, int, int); static int search(int *, int, int); static int skipline(FILE *); static int stone(int *, int, int *, int *, int); static enum readhash readhash(FILE *, int, unsigned *); static int files_differ(FILE *, FILE *, int); static char *match_function(const long *, int, FILE *); static char *preadline(int, size_t, off_t); static int *J; /* will be overlaid on class */ static int *class; /* will be overlaid on file[0] */ static int *klist; /* will be overlaid on file[0] after class */ static int *member; /* will be overlaid on file[1] */ static int clen; static int inifdef; /* whether or not we are in a #ifdef block */ static int len[2]; static int pref, suff; /* length of prefix and suffix */ static int slen[2]; static int anychange; static int hw, padding; /* half width and padding */ static int edoffset; static long *ixnew; /* will be overlaid on file[1] */ static long *ixold; /* will be overlaid on klist */ static struct cand *clist; /* merely a free storage pot for candidates */ static int clistlen; /* the length of clist */ static struct line *sfile[2]; /* shortened by pruning common prefix/suffix */ static int (*chrtran)(int); /* translation table for case-folding */ static struct context_vec *context_vec_start; static struct context_vec *context_vec_end; static struct context_vec *context_vec_ptr; #define FUNCTION_CONTEXT_SIZE 55 static char lastbuf[FUNCTION_CONTEXT_SIZE]; static int lastline; static int lastmatchline; static int clow2low(int c) { return (c); } static int cup2low(int c) { return (tolower(c)); } int diffreg(char *file1, char *file2, int flags, int capsicum) { FILE *f1, *f2; int i, rval; struct pr *pr = NULL; cap_rights_t rights_ro; f1 = f2 = NULL; rval = D_SAME; anychange = 0; lastline = 0; lastmatchline = 0; /* * hw excludes padding and make sure when -t is not used, * the second column always starts from the closest tab stop */ if (diff_format == D_SIDEBYSIDE) { hw = width >> 1; padding = tabsize - (hw % tabsize); if ((flags & D_EXPANDTABS) != 0 || (padding % tabsize == 0)) padding = MIN_PAD; hw = (width >> 1) - ((padding == MIN_PAD) ? (padding << 1) : padding) - 1; } if (flags & D_IGNORECASE) chrtran = cup2low; else chrtran = clow2low; if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode)) return (S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2); if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0) goto closem; if (flags & D_EMPTY1) f1 = fopen(_PATH_DEVNULL, "r"); else { if (!S_ISREG(stb1.st_mode)) { if ((f1 = opentemp(file1)) == NULL || fstat(fileno(f1), &stb1) == -1) { warn("%s", file1); rval = D_ERROR; status |= 2; goto closem; } } else if (strcmp(file1, "-") == 0) f1 = stdin; else f1 = fopen(file1, "r"); } if (f1 == NULL) { warn("%s", file1); rval = D_ERROR; status |= 2; goto closem; } if (flags & D_EMPTY2) f2 = fopen(_PATH_DEVNULL, "r"); else { if (!S_ISREG(stb2.st_mode)) { if ((f2 = opentemp(file2)) == NULL || fstat(fileno(f2), &stb2) == -1) { warn("%s", file2); rval = D_ERROR; status |= 2; goto closem; } } else if (strcmp(file2, "-") == 0) f2 = stdin; else f2 = fopen(file2, "r"); } if (f2 == NULL) { warn("%s", file2); rval = D_ERROR; status |= 2; goto closem; } if (lflag) pr = start_pr(file1, file2); if (capsicum) { cap_rights_init(&rights_ro, CAP_READ, CAP_FSTAT, CAP_SEEK); if (caph_rights_limit(fileno(f1), &rights_ro) < 0) err(2, "unable to limit rights on: %s", file1); if (caph_rights_limit(fileno(f2), &rights_ro) < 0) err(2, "unable to limit rights on: %s", file2); if (fileno(f1) == STDIN_FILENO || fileno(f2) == STDIN_FILENO) { /* stdin has already been limited */ if (caph_limit_stderr() == -1) err(2, "unable to limit stderr"); if (caph_limit_stdout() == -1) err(2, "unable to limit stdout"); } else if (caph_limit_stdio() == -1) err(2, "unable to limit stdio"); caph_cache_catpages(); caph_cache_tzdata(); if (caph_enter() < 0) err(2, "unable to enter capability mode"); } switch (files_differ(f1, f2, flags)) { case 0: goto closem; case 1: break; default: /* error */ rval = D_ERROR; status |= 2; goto closem; } if (diff_format == D_BRIEF && ignore_pats == NULL && (flags & (D_FOLDBLANKS|D_IGNOREBLANKS|D_IGNORECASE|D_STRIPCR)) == 0) { rval = D_DIFFER; status |= 1; goto closem; } if ((flags & D_FORCEASCII) != 0) { (void)prepare(0, f1, stb1.st_size, flags); (void)prepare(1, f2, stb2.st_size, flags); } else if (!asciifile(f1) || !asciifile(f2) || !prepare(0, f1, stb1.st_size, flags) || !prepare(1, f2, stb2.st_size, flags)) { rval = D_BINARY; status |= 1; goto closem; } prune(); sort(sfile[0], slen[0]); sort(sfile[1], slen[1]); member = (int *)file[1]; equiv(sfile[0], slen[0], sfile[1], slen[1], member); member = xreallocarray(member, slen[1] + 2, sizeof(*member)); class = (int *)file[0]; unsort(sfile[0], slen[0], class); class = xreallocarray(class, slen[0] + 2, sizeof(*class)); klist = xcalloc(slen[0] + 2, sizeof(*klist)); clen = 0; clistlen = 100; clist = xcalloc(clistlen, sizeof(*clist)); i = stone(class, slen[0], member, klist, flags); free(member); free(class); J = xreallocarray(J, len[0] + 2, sizeof(*J)); unravel(klist[i]); free(clist); free(klist); ixold = xreallocarray(ixold, len[0] + 2, sizeof(*ixold)); ixnew = xreallocarray(ixnew, len[1] + 2, sizeof(*ixnew)); check(f1, f2, flags); output(file1, f1, file2, f2, flags); closem: if (pr != NULL) stop_pr(pr); if (anychange) { status |= 1; if (rval == D_SAME) rval = D_DIFFER; } if (f1 != NULL) fclose(f1); if (f2 != NULL) fclose(f2); return (rval); } /* * Check to see if the given files differ. * Returns 0 if they are the same, 1 if different, and -1 on error. * XXX - could use code from cmp(1) [faster] */ static int files_differ(FILE *f1, FILE *f2, int flags) { char buf1[BUFSIZ], buf2[BUFSIZ]; size_t i, j; if ((flags & (D_EMPTY1|D_EMPTY2)) || stb1.st_size != stb2.st_size || (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)) return (1); if (stb1.st_dev == stb2.st_dev && stb1.st_ino == stb2.st_ino) return (0); for (;;) { i = fread(buf1, 1, sizeof(buf1), f1); j = fread(buf2, 1, sizeof(buf2), f2); if ((!i && ferror(f1)) || (!j && ferror(f2))) return (-1); if (i != j) return (1); if (i == 0) return (0); if (memcmp(buf1, buf2, i) != 0) return (1); } } static FILE * opentemp(const char *f) { char buf[BUFSIZ], tempfile[PATH_MAX]; ssize_t nread; int ifd, ofd; if (strcmp(f, "-") == 0) ifd = STDIN_FILENO; else if ((ifd = open(f, O_RDONLY, 0644)) == -1) return (NULL); (void)strlcpy(tempfile, _PATH_TMP "/diff.XXXXXXXX", sizeof(tempfile)); if ((ofd = mkstemp(tempfile)) == -1) { close(ifd); return (NULL); } unlink(tempfile); while ((nread = read(ifd, buf, BUFSIZ)) > 0) { if (write(ofd, buf, nread) != nread) { close(ifd); close(ofd); return (NULL); } } close(ifd); lseek(ofd, (off_t)0, SEEK_SET); return (fdopen(ofd, "r")); } static bool prepare(int i, FILE *fd, size_t filesize, int flags) { struct line *p; unsigned h; size_t sz, j = 0; enum readhash r; rewind(fd); sz = MIN(filesize, SIZE_MAX) / 25; if (sz < 100) sz = 100; p = xcalloc(sz + 3, sizeof(*p)); while ((r = readhash(fd, flags, &h)) != RH_EOF) switch (r) { case RH_EOF: /* otherwise clang complains */ case RH_BINARY: return (false); case RH_OK: if (j == sz) { sz = sz * 3 / 2; p = xreallocarray(p, sz + 3, sizeof(*p)); } p[++j].value = h; } len[i] = j; file[i] = p; return (true); } static void prune(void) { int i, j; for (pref = 0; pref < len[0] && pref < len[1] && file[0][pref + 1].value == file[1][pref + 1].value; pref++) ; for (suff = 0; suff < len[0] - pref && suff < len[1] - pref && file[0][len[0] - suff].value == file[1][len[1] - suff].value; suff++) ; for (j = 0; j < 2; j++) { sfile[j] = file[j] + pref; slen[j] = len[j] - pref - suff; for (i = 0; i <= slen[j]; i++) sfile[j][i].serial = i; } } static void equiv(struct line *a, int n, struct line *b, int m, int *c) { int i, j; i = j = 1; while (i <= n && j <= m) { if (a[i].value < b[j].value) a[i++].value = 0; else if (a[i].value == b[j].value) a[i++].value = j; else j++; } while (i <= n) a[i++].value = 0; b[m + 1].value = 0; j = 0; while (++j <= m) { c[j] = -b[j].serial; while (b[j + 1].value == b[j].value) { j++; c[j] = b[j].serial; } } c[j] = -1; } static int stone(int *a, int n, int *b, int *c, int flags) { int i, k, y, j, l; int oldc, tc, oldl, sq; unsigned numtries, bound; if (flags & D_MINIMAL) bound = UINT_MAX; else { sq = sqrt(n); bound = MAX(256, sq); } k = 0; c[0] = newcand(0, 0, 0); for (i = 1; i <= n; i++) { j = a[i]; if (j == 0) continue; y = -b[j]; oldl = 0; oldc = c[0]; numtries = 0; do { if (y <= clist[oldc].y) continue; l = search(c, k, y); if (l != oldl + 1) oldc = c[l - 1]; if (l <= k) { if (clist[c[l]].y <= y) continue; tc = c[l]; c[l] = newcand(i, y, oldc); oldc = tc; oldl = l; numtries++; } else { c[l] = newcand(i, y, oldc); k++; break; } } while ((y = b[++j]) > 0 && numtries < bound); } return (k); } static int newcand(int x, int y, int pred) { struct cand *q; if (clen == clistlen) { clistlen = clistlen * 11 / 10; clist = xreallocarray(clist, clistlen, sizeof(*clist)); } q = clist + clen; q->x = x; q->y = y; q->pred = pred; return (clen++); } static int search(int *c, int k, int y) { int i, j, l, t; if (clist[c[k]].y < y) /* quick look for typical case */ return (k + 1); i = 0; j = k + 1; for (;;) { l = (i + j) / 2; if (l <= i) break; t = clist[c[l]].y; if (t > y) j = l; else if (t < y) i = l; else return (l); } return (l + 1); } static void unravel(int p) { struct cand *q; int i; for (i = 0; i <= len[0]; i++) J[i] = i <= pref ? i : i > len[0] - suff ? i + len[1] - len[0] : 0; for (q = clist + p; q->y != 0; q = clist + q->pred) J[q->x + pref] = q->y + pref; } /* * Check does double duty: * 1. ferret out any fortuitous correspondences due to confounding by * hashing (which result in "jackpot") * 2. collect random access indexes to the two files */ static void check(FILE *f1, FILE *f2, int flags) { int i, j, /* jackpot, */ c, d; long ctold, ctnew; rewind(f1); rewind(f2); j = 1; ixold[0] = ixnew[0] = 0; /* jackpot = 0; */ ctold = ctnew = 0; for (i = 1; i <= len[0]; i++) { if (J[i] == 0) { ixold[i] = ctold += skipline(f1); continue; } while (j < J[i]) { ixnew[j] = ctnew += skipline(f2); j++; } if (flags & (D_FOLDBLANKS | D_IGNOREBLANKS | D_IGNORECASE | D_STRIPCR)) { for (;;) { c = getc(f1); d = getc(f2); /* * GNU diff ignores a missing newline * in one file for -b or -w. */ if (flags & (D_FOLDBLANKS | D_IGNOREBLANKS)) { if (c == EOF && d == '\n') { ctnew++; break; } else if (c == '\n' && d == EOF) { ctold++; break; } } ctold++; ctnew++; if (flags & D_STRIPCR && (c == '\r' || d == '\r')) { if (c == '\r') { if ((c = getc(f1)) == '\n') { ctold++; } else { ungetc(c, f1); } } if (d == '\r') { if ((d = getc(f2)) == '\n') { ctnew++; } else { ungetc(d, f2); } } break; } if ((flags & D_FOLDBLANKS) && isspace(c) && isspace(d)) { do { if (c == '\n') break; ctold++; } while (isspace(c = getc(f1))); do { if (d == '\n') break; ctnew++; } while (isspace(d = getc(f2))); } else if (flags & D_IGNOREBLANKS) { while (isspace(c) && c != '\n') { c = getc(f1); ctold++; } while (isspace(d) && d != '\n') { d = getc(f2); ctnew++; } } if (chrtran(c) != chrtran(d)) { /* jackpot++; */ J[i] = 0; if (c != '\n' && c != EOF) ctold += skipline(f1); if (d != '\n' && c != EOF) ctnew += skipline(f2); break; } if (c == '\n' || c == EOF) break; } } else { for (;;) { ctold++; ctnew++; if ((c = getc(f1)) != (d = getc(f2))) { /* jackpot++; */ J[i] = 0; if (c != '\n' && c != EOF) ctold += skipline(f1); if (d != '\n' && c != EOF) ctnew += skipline(f2); break; } if (c == '\n' || c == EOF) break; } } ixold[i] = ctold; ixnew[j] = ctnew; j++; } for (; j <= len[1]; j++) { ixnew[j] = ctnew += skipline(f2); } /* * if (jackpot) * fprintf(stderr, "jackpot\n"); */ } /* shellsort CACM #201 */ static void sort(struct line *a, int n) { struct line *ai, *aim, w; int j, m = 0, k; if (n == 0) return; for (j = 1; j <= n; j *= 2) m = 2 * j - 1; for (m /= 2; m != 0; m /= 2) { k = n - m; for (j = 1; j <= k; j++) { for (ai = &a[j]; ai > a; ai -= m) { aim = &ai[m]; if (aim < ai) break; /* wraparound */ if (aim->value > ai[0].value || (aim->value == ai[0].value && aim->serial > ai[0].serial)) break; w.value = ai[0].value; ai[0].value = aim->value; aim->value = w.value; w.serial = ai[0].serial; ai[0].serial = aim->serial; aim->serial = w.serial; } } } } static void unsort(struct line *f, int l, int *b) { int *a, i; a = xcalloc(l + 1, sizeof(*a)); for (i = 1; i <= l; i++) a[f[i].serial] = f[i].value; for (i = 1; i <= l; i++) b[i] = a[i]; free(a); } static int skipline(FILE *f) { int i, c; for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++) continue; return (i); } static void output(char *file1, FILE *f1, char *file2, FILE *f2, int flags) { int i, j, m, i0, i1, j0, j1, nc; rewind(f1); rewind(f2); m = len[0]; J[0] = 0; J[m + 1] = len[1] + 1; if (diff_format != D_EDIT) { for (i0 = 1; i0 <= m; i0 = i1 + 1) { while (i0 <= m && J[i0] == J[i0 - 1] + 1) { if (diff_format == D_SIDEBYSIDE && suppress_common != 1) { nc = fetch(ixold, i0, i0, f1, '\0', 1, flags); print_space(nc, (hw - nc) + (padding << 1) + 1, flags); fetch(ixnew, J[i0], J[i0], f2, '\0', 0, flags); printf("\n"); } i0++; } j0 = J[i0 - 1] + 1; i1 = i0 - 1; while (i1 < m && J[i1 + 1] == 0) i1++; j1 = J[i1 + 1] - 1; J[i1] = j1; /* * When using side-by-side, lines from both of the files are * printed. The algorithm used by diff(1) identifies the ranges * in which two files differ. * See the change() function below. * The for loop below consumes the shorter range, whereas one of * the while loops deals with the longer one. */ if (diff_format == D_SIDEBYSIDE) { for (i = i0, j = j0; i <= i1 && j <= j1; i++, j++) change(file1, f1, file2, f2, i, i, j, j, &flags); while (i <= i1) { change(file1, f1, file2, f2, i, i, j + 1, j, &flags); i++; } while (j <= j1) { change(file1, f1, file2, f2, i + 1, i, j, j, &flags); j++; } } else change(file1, f1, file2, f2, i0, i1, j0, j1, &flags); } } else { for (i0 = m; i0 >= 1; i0 = i1 - 1) { while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0) i0--; j0 = J[i0 + 1] - 1; i1 = i0 + 1; while (i1 > 1 && J[i1 - 1] == 0) i1--; j1 = J[i1 - 1] + 1; J[i1] = j1; change(file1, f1, file2, f2, i1, i0, j1, j0, &flags); } } if (m == 0) change(file1, f1, file2, f2, 1, 0, 1, len[1], &flags); if (diff_format == D_IFDEF || diff_format == D_GFORMAT) { for (;;) { #define c i0 if ((c = getc(f1)) == EOF) return; printf("%c", c); } #undef c } if (anychange != 0) { if (diff_format == D_CONTEXT) dump_context_vec(f1, f2, flags); else if (diff_format == D_UNIFIED) dump_unified_vec(f1, f2, flags); } } static void range(int a, int b, const char *separator) { printf("%d", a > b ? b : a); if (a < b) printf("%s%d", separator, b); } static void uni_range(int a, int b) { if (a < b) printf("%d,%d", a, b - a + 1); else if (a == b) printf("%d", b); else printf("%d,0", b); } static char * preadline(int fd, size_t rlen, off_t off) { char *line; ssize_t nr; line = xmalloc(rlen + 1); if ((nr = pread(fd, line, rlen, off)) == -1) err(2, "preadline"); if (nr > 0 && line[nr-1] == '\n') nr--; line[nr] = '\0'; return (line); } static bool ignoreline_pattern(char *line) { int ret; ret = regexec(&ignore_re, line, 0, NULL, 0); return (ret == 0); /* if it matched, it should be ignored. */ } static bool ignoreline(char *line, bool skip_blanks) { if (skip_blanks && *line == '\0') return (true); if (ignore_pats != NULL && ignoreline_pattern(line)) return (true); return (false); } /* * Indicate that there is a difference between lines a and b of the from file * to get to lines c to d of the to file. If a is greater then b then there * are no lines in the from file involved and this means that there were * lines appended (beginning at b). If c is greater than d then there are * lines missing from the to file. */ static void change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d, int *pflags) { static size_t max_context = 64; long curpos; int i, nc; const char *walk; bool skip_blanks, ignore; skip_blanks = (*pflags & D_SKIPBLANKLINES); restart: if ((diff_format != D_IFDEF || diff_format == D_GFORMAT) && a > b && c > d) return; if (ignore_pats != NULL || skip_blanks) { char *line; /* * All lines in the change, insert, or delete must match an ignore * pattern for the change to be ignored. */ if (a <= b) { /* Changes and deletes. */ for (i = a; i <= b; i++) { line = preadline(fileno(f1), ixold[i] - ixold[i - 1], ixold[i - 1]); ignore = ignoreline(line, skip_blanks); free(line); if (!ignore) goto proceed; } } if (a > b || c <= d) { /* Changes and inserts. */ for (i = c; i <= d; i++) { line = preadline(fileno(f2), ixnew[i] - ixnew[i - 1], ixnew[i - 1]); ignore = ignoreline(line, skip_blanks); free(line); if (!ignore) goto proceed; } } return; } proceed: if (*pflags & D_HEADER && diff_format != D_BRIEF) { printf("%s %s %s\n", diffargs, file1, file2); *pflags &= ~D_HEADER; } if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) { /* * Allocate change records as needed. */ if (context_vec_start == NULL || context_vec_ptr == context_vec_end - 1) { ptrdiff_t offset = -1; if (context_vec_start != NULL) offset = context_vec_ptr - context_vec_start; max_context <<= 1; context_vec_start = xreallocarray(context_vec_start, max_context, sizeof(*context_vec_start)); context_vec_end = context_vec_start + max_context; context_vec_ptr = context_vec_start + offset; } if (anychange == 0) { /* * Print the context/unidiff header first time through. */ print_header(file1, file2); anychange = 1; } else if (a > context_vec_ptr->b + (2 * diff_context) + 1 && c > context_vec_ptr->d + (2 * diff_context) + 1) { /* * If this change is more than 'diff_context' lines from the * previous change, dump the record and reset it. */ if (diff_format == D_CONTEXT) dump_context_vec(f1, f2, *pflags); else dump_unified_vec(f1, f2, *pflags); } context_vec_ptr++; context_vec_ptr->a = a; context_vec_ptr->b = b; context_vec_ptr->c = c; context_vec_ptr->d = d; return; } if (anychange == 0) anychange = 1; switch (diff_format) { case D_BRIEF: return; case D_NORMAL: case D_EDIT: range(a, b, ","); printf("%c", a > b ? 'a' : c > d ? 'd' : 'c'); if (diff_format == D_NORMAL) range(c, d, ","); printf("\n"); break; case D_REVERSE: printf("%c", a > b ? 'a' : c > d ? 'd' : 'c'); range(a, b, " "); printf("\n"); break; case D_NREVERSE: if (a > b) printf("a%d %d\n", b, d - c + 1); else { printf("d%d %d\n", a, b - a + 1); if (!(c > d)) /* add changed lines */ printf("a%d %d\n", b, d - c + 1); } break; } if (diff_format == D_GFORMAT) { curpos = ftell(f1); /* print through if append (a>b), else to (nb: 0 vs 1 orig) */ nc = ixold[a > b ? b : a - 1] - curpos; for (i = 0; i < nc; i++) printf("%c", getc(f1)); for (walk = group_format; *walk != '\0'; walk++) { if (*walk == '%') { walk++; switch (*walk) { case '<': fetch(ixold, a, b, f1, '<', 1, *pflags); break; case '>': fetch(ixnew, c, d, f2, '>', 0, *pflags); break; default: printf("%%%c", *walk); break; } continue; } printf("%c", *walk); } } if (diff_format == D_SIDEBYSIDE) { if (color && a > b) printf("\033[%sm", add_code); else if (color && c > d) printf("\033[%sm", del_code); if (a > b) { print_space(0, hw + padding , *pflags); } else { nc = fetch(ixold, a, b, f1, '\0', 1, *pflags); print_space(nc, hw - nc + padding, *pflags); } if (color && a > b) printf("\033[%sm", add_code); else if (color && c > d) printf("\033[%sm", del_code); printf("%c", (a > b) ? '>' : ((c > d) ? '<' : '|')); if (color && c > d) printf("\033[m"); print_space(hw + padding + 1 , padding, *pflags); fetch(ixnew, c, d, f2, '\0', 0, *pflags); printf("\n"); } if (diff_format == D_NORMAL || diff_format == D_IFDEF) { fetch(ixold, a, b, f1, '<', 1, *pflags); if (a <= b && c <= d && diff_format == D_NORMAL) printf("---\n"); } if (diff_format != D_GFORMAT && diff_format != D_SIDEBYSIDE) fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0, *pflags); if (edoffset != 0 && diff_format == D_EDIT) { /* * A non-zero edoffset value for D_EDIT indicates that the last line * printed was a bare dot (".") that has been escaped as ".." to * prevent ed(1) from misinterpreting it. We have to add a * substitute command to change this back and restart where we left * off. */ printf(".\n"); printf("%ds/.//\n", a + edoffset - 1); b = a + edoffset - 1; a = b + 1; c += edoffset; goto restart; } if ((diff_format == D_EDIT || diff_format == D_REVERSE) && c <= d) printf(".\n"); if (inifdef) { printf("#endif /* %s */\n", ifdefname); inifdef = 0; } } static int fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags) { int i, j, c, lastc, col, nc, newcol; edoffset = 0; nc = 0; /* * When doing #ifdef's, copy down to current line * if this is the first file, so that stuff makes it to output. */ if ((diff_format == D_IFDEF) && oldfile) { long curpos = ftell(lb); /* print through if append (a>b), else to (nb: 0 vs 1 orig) */ nc = f[a > b ? b : a - 1] - curpos; for (i = 0; i < nc; i++) printf("%c", getc(lb)); } if (a > b) return (0); if (diff_format == D_IFDEF) { if (inifdef) { printf("#else /* %s%s */\n", oldfile == 1 ? "!" : "", ifdefname); } else { if (oldfile) printf("#ifndef %s\n", ifdefname); else printf("#ifdef %s\n", ifdefname); } inifdef = 1 + oldfile; } for (i = a; i <= b; i++) { fseek(lb, f[i - 1], SEEK_SET); nc = f[i] - f[i - 1]; if (diff_format == D_SIDEBYSIDE && hw < nc) nc = hw; if (diff_format != D_IFDEF && diff_format != D_GFORMAT && ch != '\0') { if (color && (ch == '>' || ch == '+')) printf("\033[%sm", add_code); else if (color && (ch == '<' || ch == '-')) printf("\033[%sm", del_code); printf("%c", ch); if (Tflag && (diff_format == D_NORMAL || diff_format == D_CONTEXT || diff_format == D_UNIFIED)) printf("\t"); else if (diff_format != D_UNIFIED) printf(" "); } col = j = 0; lastc = '\0'; while (j < nc && (hw == 0 || col < hw)) { c = getc(lb); if (flags & D_STRIPCR && c == '\r') { if ((c = getc(lb)) == '\n') j++; else { ungetc(c, lb); c = '\r'; } } if (c == EOF) { if (diff_format == D_EDIT || diff_format == D_REVERSE || diff_format == D_NREVERSE) warnx("No newline at end of file"); else printf("\n\\ No newline at end of file\n"); return (col); } /* * when using --side-by-side, col needs to be increased * in any case to keep the columns aligned */ if (c == '\t') { if (flags & D_EXPANDTABS) { newcol = ((col / tabsize) + 1) * tabsize; do { printf(" "); } while (++col < newcol && col < hw); } else { if (diff_format == D_SIDEBYSIDE) { if ((col + tabsize) > hw) { printf("%*s", hw - col, ""); col = hw; } else { printf("\t"); col += tabsize - 1; } } else { printf("\t"); col++; } } } else { if (diff_format == D_EDIT && j == 1 && c == '\n' && lastc == '.') { /* * Don't print a bare "." line since that will confuse * ed(1). Print ".." instead and set the, global variable * edoffset to an offset from which to restart. The * caller must check the value of edoffset */ printf(".\n"); edoffset = i - a + 1; return (edoffset); } /* when side-by-side, do not print a newline */ if (diff_format != D_SIDEBYSIDE || c != '\n') { if (color && c == '\n') printf("\033[m%c", c); else printf("%c", c); col++; } } j++; lastc = c; } } if (color && diff_format == D_SIDEBYSIDE) printf("\033[m"); return (col); } /* * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578. */ static enum readhash readhash(FILE *f, int flags, unsigned *hash) { int i, t, space; unsigned sum; sum = 1; space = 0; for (i = 0;;) { switch (t = getc(f)) { case '\0': if ((flags & D_FORCEASCII) == 0) return (RH_BINARY); goto hashchar; case '\r': if (flags & D_STRIPCR) { t = getc(f); if (t == '\n') break; ungetc(t, f); } /* FALLTHROUGH */ case '\t': case '\v': case '\f': case ' ': if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) != 0) { space++; continue; } /* FALLTHROUGH */ default: hashchar: if (space && (flags & D_IGNOREBLANKS) == 0) { i++; space = 0; } sum = sum * 127 + chrtran(t); i++; continue; case EOF: if (i == 0) return (RH_EOF); /* FALLTHROUGH */ case '\n': break; } break; } *hash = sum; return (RH_OK); } static int asciifile(FILE *f) { unsigned char buf[BUFSIZ]; size_t cnt; if (f == NULL) return (1); rewind(f); cnt = fread(buf, 1, sizeof(buf), f); return (memchr(buf, '\0', cnt) == NULL); } #define begins_with(s, pre) (strncmp(s, pre, sizeof(pre) - 1) == 0) static char * match_function(const long *f, int pos, FILE *fp) { unsigned char buf[FUNCTION_CONTEXT_SIZE]; size_t nc; int last = lastline; const char *state = NULL; lastline = pos; for (; pos > last; pos--) { fseek(fp, f[pos - 1], SEEK_SET); nc = f[pos] - f[pos - 1]; if (nc >= sizeof(buf)) nc = sizeof(buf) - 1; nc = fread(buf, 1, nc, fp); if (nc == 0) continue; buf[nc] = '\0'; buf[strcspn(buf, "\n")] = '\0'; if (most_recent_pat != NULL) { int ret = regexec(&most_recent_re, buf, 0, NULL, 0); if (ret != 0) continue; strlcpy(lastbuf, buf, sizeof(lastbuf)); lastmatchline = pos; return (lastbuf); } else if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$' || buf[0] == '-' || buf[0] == '+') { if (begins_with(buf, "private:")) { if (!state) state = " (private)"; } else if (begins_with(buf, "protected:")) { if (!state) state = " (protected)"; } else if (begins_with(buf, "public:")) { if (!state) state = " (public)"; } else { strlcpy(lastbuf, buf, sizeof(lastbuf)); if (state) strlcat(lastbuf, state, sizeof(lastbuf)); lastmatchline = pos; return (lastbuf); } } } return (lastmatchline > 0 ? lastbuf : NULL); } /* dump accumulated "context" diff changes */ static void dump_context_vec(FILE *f1, FILE *f2, int flags) { struct context_vec *cvp = context_vec_start; int lowa, upb, lowc, upd, do_output; int a, b, c, d; char ch, *f; if (context_vec_start > context_vec_ptr) return; b = d = 0; /* gcc */ lowa = MAX(1, cvp->a - diff_context); upb = MIN(len[0], context_vec_ptr->b + diff_context); lowc = MAX(1, cvp->c - diff_context); upd = MIN(len[1], context_vec_ptr->d + diff_context); printf("***************"); if (flags & (D_PROTOTYPE | D_MATCHLAST)) { f = match_function(ixold, cvp->a - 1, f1); if (f != NULL) printf(" %s", f); } printf("\n*** "); range(lowa, upb, ","); printf(" ****\n"); /* * Output changes to the "old" file. The first loop suppresses * output if there were no changes to the "old" file (we'll see * the "old" lines as context in the "new" list). */ do_output = 0; for (; cvp <= context_vec_ptr; cvp++) if (cvp->a <= cvp->b) { cvp = context_vec_start; do_output++; break; } if (do_output) { while (cvp <= context_vec_ptr) { a = cvp->a; b = cvp->b; c = cvp->c; d = cvp->d; if (a <= b && c <= d) ch = 'c'; else ch = (a <= b) ? 'd' : 'a'; if (ch == 'a') fetch(ixold, lowa, b, f1, ' ', 0, flags); else { fetch(ixold, lowa, a - 1, f1, ' ', 0, flags); fetch(ixold, a, b, f1, ch == 'c' ? '!' : '-', 0, flags); } lowa = b + 1; cvp++; } fetch(ixold, b + 1, upb, f1, ' ', 0, flags); } /* output changes to the "new" file */ printf("--- "); range(lowc, upd, ","); printf(" ----\n"); do_output = 0; for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++) if (cvp->c <= cvp->d) { cvp = context_vec_start; do_output++; break; } if (do_output) { while (cvp <= context_vec_ptr) { a = cvp->a; b = cvp->b; c = cvp->c; d = cvp->d; if (a <= b && c <= d) ch = 'c'; else ch = (a <= b) ? 'd' : 'a'; if (ch == 'd') fetch(ixnew, lowc, d, f2, ' ', 0, flags); else { fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags); fetch(ixnew, c, d, f2, ch == 'c' ? '!' : '+', 0, flags); } lowc = d + 1; cvp++; } fetch(ixnew, d + 1, upd, f2, ' ', 0, flags); } context_vec_ptr = context_vec_start - 1; } /* dump accumulated "unified" diff changes */ static void dump_unified_vec(FILE *f1, FILE *f2, int flags) { struct context_vec *cvp = context_vec_start; int lowa, upb, lowc, upd; int a, b, c, d; char ch, *f; if (context_vec_start > context_vec_ptr) return; b = d = 0; /* gcc */ lowa = MAX(1, cvp->a - diff_context); upb = MIN(len[0], context_vec_ptr->b + diff_context); lowc = MAX(1, cvp->c - diff_context); upd = MIN(len[1], context_vec_ptr->d + diff_context); printf("@@ -"); uni_range(lowa, upb); printf(" +"); uni_range(lowc, upd); printf(" @@"); if (flags & (D_PROTOTYPE | D_MATCHLAST)) { f = match_function(ixold, cvp->a - 1, f1); if (f != NULL) printf(" %s", f); } printf("\n"); /* * Output changes in "unified" diff format--the old and new lines * are printed together. */ for (; cvp <= context_vec_ptr; cvp++) { a = cvp->a; b = cvp->b; c = cvp->c; d = cvp->d; /* * c: both new and old changes * d: only changes in the old file * a: only changes in the new file */ if (a <= b && c <= d) ch = 'c'; else ch = (a <= b) ? 'd' : 'a'; switch (ch) { case 'c': fetch(ixold, lowa, a - 1, f1, ' ', 0, flags); fetch(ixold, a, b, f1, '-', 0, flags); fetch(ixnew, c, d, f2, '+', 0, flags); break; case 'd': fetch(ixold, lowa, a - 1, f1, ' ', 0, flags); fetch(ixold, a, b, f1, '-', 0, flags); break; case 'a': fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags); fetch(ixnew, c, d, f2, '+', 0, flags); break; } lowa = b + 1; lowc = d + 1; } fetch(ixnew, d + 1, upd, f2, ' ', 0, flags); context_vec_ptr = context_vec_start - 1; } static void print_header(const char *file1, const char *file2) { const char *time_format; char buf[256]; struct tm tm1, tm2, *tm_ptr1, *tm_ptr2; int nsec1 = stb1.st_mtim.tv_nsec; int nsec2 = stb2.st_mtim.tv_nsec; time_format = "%Y-%m-%d %H:%M:%S"; if (cflag) time_format = "%c"; tm_ptr1 = localtime_r(&stb1.st_mtime, &tm1); tm_ptr2 = localtime_r(&stb2.st_mtime, &tm2); if (label[0] != NULL) printf("%s %s\n", diff_format == D_CONTEXT ? "***" : "---", label[0]); else { strftime(buf, sizeof(buf), time_format, tm_ptr1); printf("%s %s\t%s", diff_format == D_CONTEXT ? "***" : "---", file1, buf); if (!cflag) { strftime(buf, sizeof(buf), "%z", tm_ptr1); printf(".%.9d %s", nsec1, buf); } printf("\n"); } if (label[1] != NULL) printf("%s %s\n", diff_format == D_CONTEXT ? "---" : "+++", label[1]); else { strftime(buf, sizeof(buf), time_format, tm_ptr2); printf("%s %s\t%s", diff_format == D_CONTEXT ? "---" : "+++", file2, buf); if (!cflag) { strftime(buf, sizeof(buf), "%z", tm_ptr2); printf(".%.9d %s", nsec2, buf); } printf("\n"); } } /* * Prints n number of space characters either by using tab * or single space characters. * nc is the preceding number of characters */ static void print_space(int nc, int n, int flags) { int i, col; col = n; if ((flags & D_EXPANDTABS) == 0) { /* first tabstop may be closer than tabsize */ i = tabsize - (nc % tabsize); while (col >= tabsize) { printf("\t"); col -= i; i = tabsize; } } printf("%*s", col, ""); } diff --git a/usr.bin/diff3/diff3.c b/usr.bin/diff3/diff3.c index 78638ea2428c..c72ea0747589 100644 --- a/usr.bin/diff3/diff3.c +++ b/usr.bin/diff3/diff3.c @@ -1,1016 +1,1013 @@ /* $OpenBSD: diff3prog.c,v 1.11 2009/10/27 23:59:37 deraadt Exp $ */ /* * Copyright (C) Caldera International Inc. 2001-2002. * 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 and documentation 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 or owned by Caldera * International, Inc. * 4. Neither the name of Caldera International, Inc. nor the names of other * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA * INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif /* not lint */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * "from" is first in range of changed lines; "to" is last+1 * from=to=line after point of insertion for added lines. */ struct range { int from; int to; }; struct diff { #define DIFF_TYPE2 2 #define DIFF_TYPE3 3 int type; #if DEBUG char *line; #endif /* DEBUG */ /* Ranges as lines */ struct range old; struct range new; }; #define EFLAG_NONE 0 #define EFLAG_OVERLAP 1 #define EFLAG_NOOVERLAP 2 #define EFLAG_UNMERGED 3 static size_t szchanges; static struct diff *d13; static struct diff *d23; /* * "de" is used to gather editing scripts. These are later spewed out in * reverse order. Its first element must be all zero, the "old" and "new" * components of "de" contain line positions. Array overlap indicates which * sections in "de" correspond to lines that are different in all three files. */ static struct diff *de; static char *overlap; static int overlapcnt; static FILE *fp[3]; static int cline[3]; /* # of the last-read line in each file (0-2) */ /* * The latest known correspondence between line numbers of the 3 files * is stored in last[1-3]; */ static int last[4]; static int Aflag, eflag, iflag, mflag, Tflag; static int oflag; /* indicates whether to mark overlaps (-E or -X) */ static int strip_cr; static char *f1mark, *f2mark, *f3mark; static const char *oldmark = "<<<<<<<"; static const char *orgmark = "|||||||"; static const char *newmark = ">>>>>>>"; static const char *divider = "======="; static bool duplicate(struct range *, struct range *); static int edit(struct diff *, bool, int, int); static char *getchange(FILE *); static char *get_line(FILE *, size_t *); static int readin(int fd, struct diff **); static int skip(int, int, const char *); static void change(int, struct range *, bool); static void keep(int, struct range *); static void merge(int, int); static void prange(struct range *, bool); static void repos(int); static void edscript(int) __dead2; static void Ascript(int) __dead2; static void mergescript(int) __dead2; static void increase(void); static void usage(void); static void printrange(FILE *, struct range *); static const char diff3_version[] = "FreeBSD diff3 20220517"; enum { DIFFPROG_OPT, STRIPCR_OPT, HELP_OPT, VERSION_OPT }; #define DIFF_PATH "/usr/bin/diff" #define OPTIONS "3aAeEiL:mTxX" static struct option longopts[] = { { "ed", no_argument, NULL, 'e' }, { "show-overlap", no_argument, NULL, 'E' }, { "overlap-only", no_argument, NULL, 'x' }, { "initial-tab", no_argument, NULL, 'T' }, { "text", no_argument, NULL, 'a' }, { "strip-trailing-cr", no_argument, NULL, STRIPCR_OPT }, { "show-all", no_argument, NULL, 'A' }, { "easy-only", no_argument, NULL, '3' }, { "merge", no_argument, NULL, 'm' }, { "label", required_argument, NULL, 'L' }, { "diff-program", required_argument, NULL, DIFFPROG_OPT }, { "help", no_argument, NULL, HELP_OPT}, { "version", no_argument, NULL, VERSION_OPT} }; static void usage(void) { fprintf(stderr, "usage: diff3 [-3aAeEimTxX] [-L label1] [-L label2] " "[-L label3] file1 file2 file3\n"); } static int readin(int fd, struct diff **dd) { int a, b, c, d; size_t i; char kind, *p; FILE *f; f = fdopen(fd, "r"); if (f == NULL) err(2, "fdopen"); for (i = 0; (p = getchange(f)); i++) { #if DEBUG (*dd)[i].line = strdup(p); #endif /* DEBUG */ if (i >= szchanges - 1) increase(); a = b = (int)strtoimax(p, &p, 10); if (*p == ',') { p++; b = (int)strtoimax(p, &p, 10); } kind = *p++; c = d = (int)strtoimax(p, &p, 10); if (*p == ',') { p++; d = (int)strtoimax(p, &p, 10); } if (kind == 'a') a++; if (kind == 'd') c++; b++; d++; (*dd)[i].old.from = a; (*dd)[i].old.to = b; (*dd)[i].new.from = c; (*dd)[i].new.to = d; } if (i) { (*dd)[i].old.from = (*dd)[i - 1].old.to; (*dd)[i].new.from = (*dd)[i - 1].new.to; } fclose(f); return (i); } static int diffexec(const char *diffprog, char **diffargv, int fd[]) { int pd; switch (pdfork(&pd, PD_CLOEXEC)) { case 0: close(fd[0]); if (dup2(fd[1], STDOUT_FILENO) == -1) err(2, "child could not duplicate descriptor"); close(fd[1]); execvp(diffprog, diffargv); err(2, "could not execute diff: %s", diffprog); break; case -1: err(2, "could not fork"); break; } close(fd[1]); return (pd); } static char * getchange(FILE *b) { char *line; while ((line = get_line(b, NULL))) { if (isdigit((unsigned char)line[0])) return (line); } return (NULL); } static char * get_line(FILE *b, size_t *n) { ssize_t len; static char *buf = NULL; static size_t bufsize = 0; if ((len = getline(&buf, &bufsize, b)) < 0) return (NULL); if (strip_cr && len >= 2 && strcmp("\r\n", &(buf[len - 2])) == 0) { buf[len - 2] = '\n'; buf[len - 1] = '\0'; len--; } if (n != NULL) *n = len; return (buf); } static void merge(int m1, int m2) { struct diff *d1, *d2, *d3; int j, t1, t2; bool dup = false; d1 = d13; d2 = d23; j = 0; while (t1 = d1 < d13 + m1, t2 = d2 < d23 + m2, t1 || t2) { /* first file is different from the others */ if (!t2 || (t1 && d1->new.to < d2->new.from)) { /* stuff peculiar to 1st file */ if (eflag == EFLAG_NONE) { printf("====1\n"); change(1, &d1->old, false); keep(2, &d1->new); change(3, &d1->new, false); } d1++; continue; } /* second file is different from others */ if (!t1 || (t2 && d2->new.to < d1->new.from)) { if (eflag == EFLAG_NONE) { printf("====2\n"); keep(1, &d2->new); change(3, &d2->new, false); change(2, &d2->old, false); } else if (Aflag || mflag) { // XXX-THJ: What does it mean for the second file to differ? if (eflag == EFLAG_UNMERGED) j = edit(d2, dup, j, DIFF_TYPE2); } d2++; continue; } /* * Merge overlapping changes in first file * this happens after extension (see below). */ if (d1 + 1 < d13 + m1 && d1->new.to >= d1[1].new.from) { d1[1].old.from = d1->old.from; d1[1].new.from = d1->new.from; d1++; continue; } /* merge overlapping changes in second */ if (d2 + 1 < d23 + m2 && d2->new.to >= d2[1].new.from) { d2[1].old.from = d2->old.from; d2[1].new.from = d2->new.from; d2++; continue; } /* stuff peculiar to third file or different in all */ if (d1->new.from == d2->new.from && d1->new.to == d2->new.to) { dup = duplicate(&d1->old, &d2->old); /* * dup = 0 means all files differ * dup = 1 means files 1 and 2 identical */ if (eflag == EFLAG_NONE) { printf("====%s\n", dup ? "3" : ""); change(1, &d1->old, dup); change(2, &d2->old, false); d3 = d1->old.to > d1->old.from ? d1 : d2; change(3, &d3->new, false); } else { j = edit(d1, dup, j, DIFF_TYPE3); } dup = false; d1++; d2++; continue; } /* * Overlapping changes from file 1 and 2; extend changes * appropriately to make them coincide. */ if (d1->new.from < d2->new.from) { d2->old.from -= d2->new.from - d1->new.from; d2->new.from = d1->new.from; } else if (d2->new.from < d1->new.from) { d1->old.from -= d1->new.from - d2->new.from; d1->new.from = d2->new.from; } if (d1->new.to > d2->new.to) { d2->old.to += d1->new.to - d2->new.to; d2->new.to = d1->new.to; } else if (d2->new.to > d1->new.to) { d1->old.to += d2->new.to - d1->new.to; d1->new.to = d2->new.to; } } if (mflag) mergescript(j); else if (Aflag) Ascript(j); else if (eflag) edscript(j); } /* * The range of lines rold.from thru rold.to in file i is to be changed. * It is to be printed only if it does not duplicate something to be * printed later. */ static void change(int i, struct range *rold, bool dup) { printf("%d:", i); last[i] = rold->to; prange(rold, false); if (dup) return; i--; skip(i, rold->from, NULL); skip(i, rold->to, " "); } /* * Print the range of line numbers, rold.from thru rold.to, as n1,n2 or * n1. */ static void prange(struct range *rold, bool delete) { if (rold->to <= rold->from) printf("%da\n", rold->from - 1); else { printf("%d", rold->from); if (rold->to > rold->from + 1) printf(",%d", rold->to - 1); if (delete) printf("d\n"); else printf("c\n"); } } /* * No difference was reported by diff between file 1 (or 2) and file 3, * and an artificial dummy difference (trange) must be ginned up to * correspond to the change reported in the other file. */ static void keep(int i, struct range *rnew) { int delta; struct range trange; delta = last[3] - last[i]; trange.from = rnew->from - delta; trange.to = rnew->to - delta; change(i, &trange, true); } /* * skip to just before line number from in file "i". If "pr" is non-NULL, * print all skipped stuff with string pr as a prefix. */ static int skip(int i, int from, const char *pr) { size_t j, n; char *line; for (n = 0; cline[i] < from - 1; n += j) { if ((line = get_line(fp[i], &j)) == NULL) errx(EXIT_FAILURE, "logic error"); if (pr != NULL) printf("%s%s", Tflag == 1 ? "\t" : pr, line); cline[i]++; } return ((int) n); } /* * Return 1 or 0 according as the old range (in file 1) contains exactly * the same data as the new range (in file 2). */ static bool duplicate(struct range *r1, struct range *r2) { int c, d; int nchar; int nline; if (r1->to-r1->from != r2->to-r2->from) return (0); skip(0, r1->from, NULL); skip(1, r2->from, NULL); nchar = 0; for (nline = 0; nline < r1->to - r1->from; nline++) { do { c = getc(fp[0]); d = getc(fp[1]); if (c == -1 && d == -1) break; if (c == -1 || d == -1) errx(EXIT_FAILURE, "logic error"); nchar++; if (c != d) { repos(nchar); return (0); } } while (c != '\n'); } repos(nchar); return (1); } static void repos(int nchar) { int i; for (i = 0; i < 2; i++) (void)fseek(fp[i], (long)-nchar, SEEK_CUR); } /* * collect an editing script for later regurgitation */ static int edit(struct diff *diff, bool dup, int j, int difftype) { if (!(eflag == EFLAG_UNMERGED || (!dup && eflag == EFLAG_OVERLAP ) || (dup && eflag == EFLAG_NOOVERLAP))) { return (j); } j++; overlap[j] = !dup; if (!dup) overlapcnt++; de[j].type = difftype; #if DEBUG de[j].line = strdup(diff->line); #endif /* DEBUG */ de[j].old.from = diff->old.from; de[j].old.to = diff->old.to; de[j].new.from = diff->new.from; de[j].new.to = diff->new.to; return (j); } static void printrange(FILE *p, struct range *r) { char *line = NULL; size_t len = 0; int i = 1; ssize_t rlen = 0; /* We haven't been asked to print anything */ if (r->from == r->to) return; if (r->from > r->to) errx(EXIT_FAILURE, "invalid print range"); /* * XXX-THJ: We read through all of the file for each range printed. * This duplicates work and will probably impact performance on large * files with lots of ranges. */ fseek(p, 0L, SEEK_SET); while ((rlen = getline(&line, &len, p)) > 0) { if (i >= r->from) printf("%s", line); if (++i > r->to - 1) break; } free(line); } /* regurgitate */ static void edscript(int n) { bool delete; struct range *new, *old; for (; n > 0; n--) { new = &de[n].new; old = &de[n].old; delete = (new->from == new->to); if (!oflag || !overlap[n]) { prange(old, delete); } else { printf("%da\n", old->to - 1); printf("%s\n", divider); } printrange(fp[2], new); if (!oflag || !overlap[n]) { if (!delete) printf(".\n"); } else { printf("%s %s\n.\n", newmark, f3mark); printf("%da\n%s %s\n.\n", old->from - 1, oldmark, f1mark); } } if (iflag) printf("w\nq\n"); exit(eflag == EFLAG_NONE ? overlapcnt : 0); } /* * Output an edit script to turn mine into yours, when there is a conflict * between the 3 files bracket the changes. Regurgitate the diffs in reverse * order to allow the ed script to track down where the lines are as changes * are made. */ static void Ascript(int n) { int startmark; bool deletenew; bool deleteold; struct range *new, *old; for (; n > 0; n--) { new = &de[n].new; old = &de[n].old; deletenew = (new->from == new->to); deleteold = (old->from == old->to); if (de[n].type == DIFF_TYPE2) { if (!oflag || !overlap[n]) { prange(old, deletenew); printrange(fp[2], new); } else { startmark = new->to; if (!deletenew) startmark--; printf("%da\n", startmark); printf("%s %s\n", newmark, f3mark); printf(".\n"); printf("%da\n", startmark - (new->to - new->from)); printf("%s %s\n", oldmark, f2mark); if (!deleteold) printrange(fp[1], old); printf("%s\n.\n", divider); } } else if (de[n].type == DIFF_TYPE3) { startmark = old->to - 1; if (!oflag || !overlap[n]) { prange(old, deletenew); printrange(fp[2], new); } else { printf("%da\n", startmark); printf("%s %s\n", orgmark, f2mark); if (deleteold) { struct range r; r.from = old->from-1; r.to = new->to; printrange(fp[1], &r); } else printrange(fp[1], old); printf("%s\n", divider); printrange(fp[2], new); } if (!oflag || !overlap[n]) { if (!deletenew) printf(".\n"); } else { printf("%s %s\n.\n", newmark, f3mark); /* * Go to the start of the conflict in original * file and append lines */ printf("%da\n%s %s\n.\n", startmark - (old->to - old->from), oldmark, f1mark); } } } if (iflag) printf("w\nq\n"); exit(overlapcnt > 0); } /* * Output the merged file directly (don't generate an ed script). When * regurgitating diffs we need to walk forward through the file and print any * inbetween lines. */ static void mergescript(int i) { struct range r, *new, *old; int n; r.from = 1; r.to = 1; for (n = 1; n < i+1; n++) { new = &de[n].new; old = &de[n].old; /* print any lines leading up to here */ r.to = old->from; printrange(fp[0], &r); if (de[n].type == DIFF_TYPE2) { printf("%s %s\n", oldmark, f2mark); printrange(fp[1], old); printf("%s\n", divider); printrange(fp[2], new); printf("%s %s\n", newmark, f3mark); } else if (de[n].type == DIFF_TYPE3) { if (!oflag || !overlap[n]) { printrange(fp[2], new); } else { printf("%s %s\n", oldmark, f1mark); printrange(fp[0], old); printf("%s %s\n", orgmark, f2mark); if (old->from == old->to) { struct range or; or.from = old->from - 1; or.to = new->to; printrange(fp[1], &or); } else printrange(fp[1], old); printf("%s\n", divider); printrange(fp[2], new); printf("%s %s\n", newmark, f3mark); } } if (old->from == old->to) r.from = new->to; else r.from = old->to; } /* * Print from the final range to the end of 'myfile'. Any deletions or * additions to this file should have been handled by now. * * If the ranges are the same we need to rewind a line. * If the new range is 0 length (from == to), we need to use the old * range. */ new = &de[n-1].new; old = &de[n-1].old; if ((old->from == new->from) && (old->to == new->to)) r.from--; else if (new->from == new->to) r.from = old->from; /* * If the range is a 3 way merge then we need to skip a line in the * trailing output. */ if (de[n-1].type == DIFF_TYPE3) r.from++; r.to = INT_MAX; printrange(fp[0], &r); exit(overlapcnt > 0); } static void increase(void) { struct diff *p; char *q; size_t newsz, incr; /* are the memset(3) calls needed? */ newsz = szchanges == 0 ? 64 : 2 * szchanges; incr = newsz - szchanges; p = reallocarray(d13, newsz, sizeof(struct diff)); if (p == NULL) err(1, NULL); memset(p + szchanges, 0, incr * sizeof(struct diff)); d13 = p; p = reallocarray(d23, newsz, sizeof(struct diff)); if (p == NULL) err(1, NULL); memset(p + szchanges, 0, incr * sizeof(struct diff)); d23 = p; p = reallocarray(de, newsz, sizeof(struct diff)); if (p == NULL) err(1, NULL); memset(p + szchanges, 0, incr * sizeof(struct diff)); de = p; q = reallocarray(overlap, newsz, sizeof(char)); if (q == NULL) err(1, NULL); memset(q + szchanges, 0, incr * sizeof(char)); overlap = q; szchanges = newsz; } int main(int argc, char **argv) { int ch, nblabels, status, m, n, kq, nke, nleft, i; char *labels[] = { NULL, NULL, NULL }; const char *diffprog = DIFF_PATH; char *file1, *file2, *file3; char *diffargv[7]; int diffargc = 0; int fd13[2], fd23[2]; int pd13, pd23; cap_rights_t rights_ro; struct kevent *e; nblabels = 0; eflag = EFLAG_NONE; oflag = 0; diffargv[diffargc++] = __DECONST(char *, diffprog); while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) { switch (ch) { case '3': eflag = EFLAG_NOOVERLAP; break; case 'a': diffargv[diffargc++] = __DECONST(char *, "-a"); break; case 'A': Aflag = 1; break; case 'e': eflag = EFLAG_UNMERGED; break; case 'E': eflag = EFLAG_OVERLAP; oflag = 1; break; case 'i': iflag = 1; break; case 'L': oflag = 1; if (nblabels >= 3) errx(2, "too many file label options"); labels[nblabels++] = optarg; break; case 'm': Aflag = 1; oflag = 1; mflag = 1; break; case 'T': Tflag = 1; break; case 'x': eflag = EFLAG_OVERLAP; break; case 'X': oflag = 1; eflag = EFLAG_OVERLAP; break; case DIFFPROG_OPT: diffprog = optarg; break; case STRIPCR_OPT: strip_cr = 1; diffargv[diffargc++] = __DECONST(char *, "--strip-trailing-cr"); break; case HELP_OPT: usage(); exit(0); case VERSION_OPT: printf("%s\n", diff3_version); exit(0); } } argc -= optind; argv += optind; if (Aflag) { if (eflag == EFLAG_NONE) eflag = EFLAG_UNMERGED; oflag = 1; } if (argc != 3) { usage(); exit(2); } if (caph_limit_stdio() == -1) err(2, "unable to limit stdio"); cap_rights_init(&rights_ro, CAP_READ, CAP_FSTAT, CAP_SEEK); kq = kqueue(); if (kq == -1) err(2, "kqueue"); e = malloc(2 * sizeof(struct kevent)); if (e == NULL) err(2, "malloc"); /* TODO stdio */ file1 = argv[0]; file2 = argv[1]; file3 = argv[2]; if (oflag) { asprintf(&f1mark, "%s", labels[0] != NULL ? labels[0] : file1); if (f1mark == NULL) err(2, "asprintf"); asprintf(&f2mark, "%s", labels[1] != NULL ? labels[1] : file2); if (f2mark == NULL) err(2, "asprintf"); asprintf(&f3mark, "%s", labels[2] != NULL ? labels[2] : file3); if (f3mark == NULL) err(2, "asprintf"); } fp[0] = fopen(file1, "r"); if (fp[0] == NULL) err(2, "Can't open %s", file1); if (caph_rights_limit(fileno(fp[0]), &rights_ro) < 0) err(2, "unable to limit rights on: %s", file1); fp[1] = fopen(file2, "r"); if (fp[1] == NULL) err(2, "Can't open %s", file2); if (caph_rights_limit(fileno(fp[1]), &rights_ro) < 0) err(2, "unable to limit rights on: %s", file2); fp[2] = fopen(file3, "r"); if (fp[2] == NULL) err(2, "Can't open %s", file3); if (caph_rights_limit(fileno(fp[2]), &rights_ro) < 0) err(2, "unable to limit rights on: %s", file3); if (pipe(fd13)) err(2, "pipe"); if (pipe(fd23)) err(2, "pipe"); diffargv[diffargc] = file1; diffargv[diffargc + 1] = file3; diffargv[diffargc + 2] = NULL; nleft = 0; pd13 = diffexec(diffprog, diffargv, fd13); EV_SET(e + nleft , pd13, EVFILT_PROCDESC, EV_ADD, NOTE_EXIT, 0, NULL); if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) err(2, "kevent1"); nleft++; diffargv[diffargc] = file2; pd23 = diffexec(diffprog, diffargv, fd23); EV_SET(e + nleft , pd23, EVFILT_PROCDESC, EV_ADD, NOTE_EXIT, 0, NULL); if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) err(2, "kevent2"); nleft++; caph_cache_catpages(); if (caph_enter() < 0) err(2, "unable to enter capability mode"); /* parse diffs */ increase(); m = readin(fd13[0], &d13); n = readin(fd23[0], &d23); /* waitpid cooked over pdforks */ while (nleft > 0) { nke = kevent(kq, NULL, 0, e, nleft, NULL); if (nke == -1) err(2, "kevent"); for (i = 0; i < nke; i++) { status = e[i].data; if (WIFEXITED(status) && WEXITSTATUS(status) >= 2) errx(2, "diff exited abnormally"); else if (WIFSIGNALED(status)) errx(2, "diff killed by signal %d", WTERMSIG(status)); } nleft -= nke; } merge(m, n); return (EXIT_SUCCESS); } diff --git a/usr.bin/elfdump/elfdump.c b/usr.bin/elfdump/elfdump.c index e5d5bccc414e..49704cde1b08 100644 --- a/usr.bin/elfdump/elfdump.c +++ b/usr.bin/elfdump/elfdump.c @@ -1,1288 +1,1287 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2003 David O'Brien. All rights reserved. * Copyright (c) 2001 Jake Burkholder * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ED_DYN (1<<0) #define ED_EHDR (1<<1) #define ED_GOT (1<<2) #define ED_HASH (1<<3) #define ED_INTERP (1<<4) #define ED_NOTE (1<<5) #define ED_PHDR (1<<6) #define ED_REL (1<<7) #define ED_SHDR (1<<8) #define ED_SYMTAB (1<<9) #define ED_ALL ((1<<10)-1) #define ED_IS_ELF (1<<10) /* Exclusive with other flags */ #define elf_get_addr elf_get_quad #define elf_get_off elf_get_quad #define elf_get_size elf_get_quad enum elf_member { D_TAG = 1, D_PTR, D_VAL, E_CLASS, E_DATA, E_OSABI, E_TYPE, E_MACHINE, E_VERSION, E_ENTRY, E_PHOFF, E_SHOFF, E_FLAGS, E_EHSIZE, E_PHENTSIZE, E_PHNUM, E_SHENTSIZE, E_SHNUM, E_SHSTRNDX, N_NAMESZ, N_DESCSZ, N_TYPE, P_TYPE, P_OFFSET, P_VADDR, P_PADDR, P_FILESZ, P_MEMSZ, P_FLAGS, P_ALIGN, SH_NAME, SH_TYPE, SH_FLAGS, SH_ADDR, SH_OFFSET, SH_SIZE, SH_LINK, SH_INFO, SH_ADDRALIGN, SH_ENTSIZE, ST_NAME, ST_VALUE, ST_SIZE, ST_INFO, ST_SHNDX, R_OFFSET, R_INFO, RA_OFFSET, RA_INFO, RA_ADDEND }; typedef enum elf_member elf_member_t; static int elf32_offsets[] = { 0, offsetof(Elf32_Dyn, d_tag), offsetof(Elf32_Dyn, d_un.d_ptr), offsetof(Elf32_Dyn, d_un.d_val), offsetof(Elf32_Ehdr, e_ident[EI_CLASS]), offsetof(Elf32_Ehdr, e_ident[EI_DATA]), offsetof(Elf32_Ehdr, e_ident[EI_OSABI]), offsetof(Elf32_Ehdr, e_type), offsetof(Elf32_Ehdr, e_machine), offsetof(Elf32_Ehdr, e_version), offsetof(Elf32_Ehdr, e_entry), offsetof(Elf32_Ehdr, e_phoff), offsetof(Elf32_Ehdr, e_shoff), offsetof(Elf32_Ehdr, e_flags), offsetof(Elf32_Ehdr, e_ehsize), offsetof(Elf32_Ehdr, e_phentsize), offsetof(Elf32_Ehdr, e_phnum), offsetof(Elf32_Ehdr, e_shentsize), offsetof(Elf32_Ehdr, e_shnum), offsetof(Elf32_Ehdr, e_shstrndx), offsetof(Elf_Note, n_namesz), offsetof(Elf_Note, n_descsz), offsetof(Elf_Note, n_type), offsetof(Elf32_Phdr, p_type), offsetof(Elf32_Phdr, p_offset), offsetof(Elf32_Phdr, p_vaddr), offsetof(Elf32_Phdr, p_paddr), offsetof(Elf32_Phdr, p_filesz), offsetof(Elf32_Phdr, p_memsz), offsetof(Elf32_Phdr, p_flags), offsetof(Elf32_Phdr, p_align), offsetof(Elf32_Shdr, sh_name), offsetof(Elf32_Shdr, sh_type), offsetof(Elf32_Shdr, sh_flags), offsetof(Elf32_Shdr, sh_addr), offsetof(Elf32_Shdr, sh_offset), offsetof(Elf32_Shdr, sh_size), offsetof(Elf32_Shdr, sh_link), offsetof(Elf32_Shdr, sh_info), offsetof(Elf32_Shdr, sh_addralign), offsetof(Elf32_Shdr, sh_entsize), offsetof(Elf32_Sym, st_name), offsetof(Elf32_Sym, st_value), offsetof(Elf32_Sym, st_size), offsetof(Elf32_Sym, st_info), offsetof(Elf32_Sym, st_shndx), offsetof(Elf32_Rel, r_offset), offsetof(Elf32_Rel, r_info), offsetof(Elf32_Rela, r_offset), offsetof(Elf32_Rela, r_info), offsetof(Elf32_Rela, r_addend) }; static int elf64_offsets[] = { 0, offsetof(Elf64_Dyn, d_tag), offsetof(Elf64_Dyn, d_un.d_ptr), offsetof(Elf64_Dyn, d_un.d_val), offsetof(Elf32_Ehdr, e_ident[EI_CLASS]), offsetof(Elf32_Ehdr, e_ident[EI_DATA]), offsetof(Elf32_Ehdr, e_ident[EI_OSABI]), offsetof(Elf64_Ehdr, e_type), offsetof(Elf64_Ehdr, e_machine), offsetof(Elf64_Ehdr, e_version), offsetof(Elf64_Ehdr, e_entry), offsetof(Elf64_Ehdr, e_phoff), offsetof(Elf64_Ehdr, e_shoff), offsetof(Elf64_Ehdr, e_flags), offsetof(Elf64_Ehdr, e_ehsize), offsetof(Elf64_Ehdr, e_phentsize), offsetof(Elf64_Ehdr, e_phnum), offsetof(Elf64_Ehdr, e_shentsize), offsetof(Elf64_Ehdr, e_shnum), offsetof(Elf64_Ehdr, e_shstrndx), offsetof(Elf_Note, n_namesz), offsetof(Elf_Note, n_descsz), offsetof(Elf_Note, n_type), offsetof(Elf64_Phdr, p_type), offsetof(Elf64_Phdr, p_offset), offsetof(Elf64_Phdr, p_vaddr), offsetof(Elf64_Phdr, p_paddr), offsetof(Elf64_Phdr, p_filesz), offsetof(Elf64_Phdr, p_memsz), offsetof(Elf64_Phdr, p_flags), offsetof(Elf64_Phdr, p_align), offsetof(Elf64_Shdr, sh_name), offsetof(Elf64_Shdr, sh_type), offsetof(Elf64_Shdr, sh_flags), offsetof(Elf64_Shdr, sh_addr), offsetof(Elf64_Shdr, sh_offset), offsetof(Elf64_Shdr, sh_size), offsetof(Elf64_Shdr, sh_link), offsetof(Elf64_Shdr, sh_info), offsetof(Elf64_Shdr, sh_addralign), offsetof(Elf64_Shdr, sh_entsize), offsetof(Elf64_Sym, st_name), offsetof(Elf64_Sym, st_value), offsetof(Elf64_Sym, st_size), offsetof(Elf64_Sym, st_info), offsetof(Elf64_Sym, st_shndx), offsetof(Elf64_Rel, r_offset), offsetof(Elf64_Rel, r_info), offsetof(Elf64_Rela, r_offset), offsetof(Elf64_Rela, r_info), offsetof(Elf64_Rela, r_addend) }; /* http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#tag_encodings */ static const char * d_tags(u_int64_t tag) { static char unknown_tag[48]; switch (tag) { case DT_NULL: return "DT_NULL"; case DT_NEEDED: return "DT_NEEDED"; case DT_PLTRELSZ: return "DT_PLTRELSZ"; case DT_PLTGOT: return "DT_PLTGOT"; case DT_HASH: return "DT_HASH"; case DT_STRTAB: return "DT_STRTAB"; case DT_SYMTAB: return "DT_SYMTAB"; case DT_RELA: return "DT_RELA"; case DT_RELASZ: return "DT_RELASZ"; case DT_RELAENT: return "DT_RELAENT"; case DT_STRSZ: return "DT_STRSZ"; case DT_SYMENT: return "DT_SYMENT"; case DT_INIT: return "DT_INIT"; case DT_FINI: return "DT_FINI"; case DT_SONAME: return "DT_SONAME"; case DT_RPATH: return "DT_RPATH"; case DT_SYMBOLIC: return "DT_SYMBOLIC"; case DT_REL: return "DT_REL"; case DT_RELSZ: return "DT_RELSZ"; case DT_RELENT: return "DT_RELENT"; case DT_PLTREL: return "DT_PLTREL"; case DT_DEBUG: return "DT_DEBUG"; case DT_TEXTREL: return "DT_TEXTREL"; case DT_JMPREL: return "DT_JMPREL"; case DT_BIND_NOW: return "DT_BIND_NOW"; case DT_INIT_ARRAY: return "DT_INIT_ARRAY"; case DT_FINI_ARRAY: return "DT_FINI_ARRAY"; case DT_INIT_ARRAYSZ: return "DT_INIT_ARRAYSZ"; case DT_FINI_ARRAYSZ: return "DT_FINI_ARRAYSZ"; case DT_RUNPATH: return "DT_RUNPATH"; case DT_FLAGS: return "DT_FLAGS"; case DT_PREINIT_ARRAY: return "DT_PREINIT_ARRAY"; /* XXX DT_ENCODING */ case DT_PREINIT_ARRAYSZ:return "DT_PREINIT_ARRAYSZ"; /* 0x6000000D - 0x6ffff000 operating system-specific semantics */ case 0x6ffffdf5: return "DT_GNU_PRELINKED"; case 0x6ffffdf6: return "DT_GNU_CONFLICTSZ"; case 0x6ffffdf7: return "DT_GNU_LIBLISTSZ"; case 0x6ffffdf8: return "DT_SUNW_CHECKSUM"; case DT_PLTPADSZ: return "DT_PLTPADSZ"; case DT_MOVEENT: return "DT_MOVEENT"; case DT_MOVESZ: return "DT_MOVESZ"; case DT_FEATURE: return "DT_FEATURE"; case DT_POSFLAG_1: return "DT_POSFLAG_1"; case DT_SYMINSZ: return "DT_SYMINSZ"; case DT_SYMINENT : return "DT_SYMINENT (DT_VALRNGHI)"; case DT_ADDRRNGLO: return "DT_ADDRRNGLO"; case DT_GNU_HASH: return "DT_GNU_HASH"; case 0x6ffffef8: return "DT_GNU_CONFLICT"; case 0x6ffffef9: return "DT_GNU_LIBLIST"; case DT_CONFIG: return "DT_CONFIG"; case DT_DEPAUDIT: return "DT_DEPAUDIT"; case DT_AUDIT: return "DT_AUDIT"; case DT_PLTPAD: return "DT_PLTPAD"; case DT_MOVETAB: return "DT_MOVETAB"; case DT_SYMINFO : return "DT_SYMINFO (DT_ADDRRNGHI)"; case DT_RELACOUNT: return "DT_RELACOUNT"; case DT_RELCOUNT: return "DT_RELCOUNT"; case DT_FLAGS_1: return "DT_FLAGS_1"; case DT_VERDEF: return "DT_VERDEF"; case DT_VERDEFNUM: return "DT_VERDEFNUM"; case DT_VERNEED: return "DT_VERNEED"; case DT_VERNEEDNUM: return "DT_VERNEEDNUM"; case 0x6ffffff0: return "DT_GNU_VERSYM"; /* 0x70000000 - 0x7fffffff processor-specific semantics */ case 0x70000000: return "DT_IA_64_PLT_RESERVE"; case DT_AUXILIARY: return "DT_AUXILIARY"; case DT_USED: return "DT_USED"; case DT_FILTER: return "DT_FILTER"; } snprintf(unknown_tag, sizeof(unknown_tag), "ERROR: TAG NOT DEFINED -- tag 0x%jx", (uintmax_t)tag); return (unknown_tag); } static const char * e_machines(u_int mach) { static char machdesc[64]; switch (mach) { case EM_NONE: return "EM_NONE"; case EM_M32: return "EM_M32"; case EM_SPARC: return "EM_SPARC"; case EM_386: return "EM_386"; case EM_68K: return "EM_68K"; case EM_88K: return "EM_88K"; case EM_IAMCU: return "EM_IAMCU"; case EM_860: return "EM_860"; case EM_MIPS: return "EM_MIPS"; case EM_PPC: return "EM_PPC"; case EM_PPC64: return "EM_PPC64"; case EM_ARM: return "EM_ARM"; case EM_ALPHA: return "EM_ALPHA (legacy)"; case EM_SPARCV9:return "EM_SPARCV9"; case EM_IA_64: return "EM_IA_64"; case EM_X86_64: return "EM_X86_64"; case EM_AARCH64:return "EM_AARCH64"; case EM_RISCV: return "EM_RISCV"; } snprintf(machdesc, sizeof(machdesc), "(unknown machine) -- type 0x%x", mach); return (machdesc); } static const char *e_types[] = { "ET_NONE", "ET_REL", "ET_EXEC", "ET_DYN", "ET_CORE" }; static const char *ei_versions[] = { "EV_NONE", "EV_CURRENT" }; static const char *ei_classes[] = { "ELFCLASSNONE", "ELFCLASS32", "ELFCLASS64" }; static const char *ei_data[] = { "ELFDATANONE", "ELFDATA2LSB", "ELFDATA2MSB" }; static const char *ei_abis[256] = { "ELFOSABI_NONE", "ELFOSABI_HPUX", "ELFOSABI_NETBSD", "ELFOSABI_LINUX", "ELFOSABI_HURD", "ELFOSABI_86OPEN", "ELFOSABI_SOLARIS", "ELFOSABI_AIX", "ELFOSABI_IRIX", "ELFOSABI_FREEBSD", "ELFOSABI_TRU64", "ELFOSABI_MODESTO", "ELFOSABI_OPENBSD", [255] = "ELFOSABI_STANDALONE" }; static const char *p_types[] = { "PT_NULL", "PT_LOAD", "PT_DYNAMIC", "PT_INTERP", "PT_NOTE", "PT_SHLIB", "PT_PHDR", "PT_TLS" }; static const char *p_flags[] = { "", "PF_X", "PF_W", "PF_X|PF_W", "PF_R", "PF_X|PF_R", "PF_W|PF_R", "PF_X|PF_W|PF_R" }; #define NT_ELEM(x) [x] = #x, static const char *nt_types[] = { "", NT_ELEM(NT_FREEBSD_ABI_TAG) NT_ELEM(NT_FREEBSD_NOINIT_TAG) NT_ELEM(NT_FREEBSD_ARCH_TAG) NT_ELEM(NT_FREEBSD_FEATURE_CTL) }; /* http://www.sco.com/developers/gabi/latest/ch4.sheader.html#sh_type */ static const char * sh_types(uint64_t machine, uint64_t sht) { static char unknown_buf[64]; if (sht < 0x60000000) { switch (sht) { case SHT_NULL: return "SHT_NULL"; case SHT_PROGBITS: return "SHT_PROGBITS"; case SHT_SYMTAB: return "SHT_SYMTAB"; case SHT_STRTAB: return "SHT_STRTAB"; case SHT_RELA: return "SHT_RELA"; case SHT_HASH: return "SHT_HASH"; case SHT_DYNAMIC: return "SHT_DYNAMIC"; case SHT_NOTE: return "SHT_NOTE"; case SHT_NOBITS: return "SHT_NOBITS"; case SHT_REL: return "SHT_REL"; case SHT_SHLIB: return "SHT_SHLIB"; case SHT_DYNSYM: return "SHT_DYNSYM"; case SHT_INIT_ARRAY: return "SHT_INIT_ARRAY"; case SHT_FINI_ARRAY: return "SHT_FINI_ARRAY"; case SHT_PREINIT_ARRAY: return "SHT_PREINIT_ARRAY"; case SHT_GROUP: return "SHT_GROUP"; case SHT_SYMTAB_SHNDX: return "SHT_SYMTAB_SHNDX"; } snprintf(unknown_buf, sizeof(unknown_buf), "ERROR: SHT %ju NOT DEFINED", (uintmax_t)sht); return (unknown_buf); } else if (sht < 0x70000000) { /* 0x60000000-0x6fffffff operating system-specific semantics */ switch (sht) { case 0x6ffffff0: return "XXX:VERSYM"; case SHT_SUNW_dof: return "SHT_SUNW_dof"; case SHT_GNU_HASH: return "SHT_GNU_HASH"; case 0x6ffffff7: return "SHT_GNU_LIBLIST"; case 0x6ffffffc: return "XXX:VERDEF"; case SHT_SUNW_verdef: return "SHT_SUNW(GNU)_verdef"; case SHT_SUNW_verneed: return "SHT_SUNW(GNU)_verneed"; case SHT_SUNW_versym: return "SHT_SUNW(GNU)_versym"; } snprintf(unknown_buf, sizeof(unknown_buf), "ERROR: OS-SPECIFIC SHT 0x%jx NOT DEFINED", (uintmax_t)sht); return (unknown_buf); } else if (sht < 0x80000000) { /* 0x70000000-0x7fffffff processor-specific semantics */ switch (machine) { case EM_ARM: switch (sht) { case SHT_ARM_EXIDX: return "SHT_ARM_EXIDX"; case SHT_ARM_PREEMPTMAP:return "SHT_ARM_PREEMPTMAP"; case SHT_ARM_ATTRIBUTES:return "SHT_ARM_ATTRIBUTES"; case SHT_ARM_DEBUGOVERLAY: return "SHT_ARM_DEBUGOVERLAY"; case SHT_ARM_OVERLAYSECTION: return "SHT_ARM_OVERLAYSECTION"; } break; case EM_IA_64: switch (sht) { case 0x70000000: return "SHT_IA_64_EXT"; case 0x70000001: return "SHT_IA_64_UNWIND"; } break; case EM_MIPS: switch (sht) { case SHT_MIPS_REGINFO: return "SHT_MIPS_REGINFO"; case SHT_MIPS_OPTIONS: return "SHT_MIPS_OPTIONS"; case SHT_MIPS_ABIFLAGS: return "SHT_MIPS_ABIFLAGS"; } break; } switch (sht) { case 0x7ffffffd: return "XXX:AUXILIARY"; case 0x7fffffff: return "XXX:FILTER"; } snprintf(unknown_buf, sizeof(unknown_buf), "ERROR: PROCESSOR-SPECIFIC SHT 0x%jx NOT DEFINED", (uintmax_t)sht); return (unknown_buf); } else { /* 0x80000000-0xffffffff application programs */ snprintf(unknown_buf, sizeof(unknown_buf), "ERROR: SHT 0x%jx NOT DEFINED", (uintmax_t)sht); return (unknown_buf); } } static const char *sh_flags[] = { "", "SHF_WRITE", "SHF_ALLOC", "SHF_WRITE|SHF_ALLOC", "SHF_EXECINSTR", "SHF_WRITE|SHF_EXECINSTR", "SHF_ALLOC|SHF_EXECINSTR", "SHF_WRITE|SHF_ALLOC|SHF_EXECINSTR" }; static const char * st_type(unsigned int mach, unsigned int type) { static char s_type[32]; switch (type) { case STT_NOTYPE: return "STT_NOTYPE"; case STT_OBJECT: return "STT_OBJECT"; case STT_FUNC: return "STT_FUNC"; case STT_SECTION: return "STT_SECTION"; case STT_FILE: return "STT_FILE"; case STT_COMMON: return "STT_COMMON"; case STT_TLS: return "STT_TLS"; case 13: if (mach == EM_SPARCV9) return "STT_SPARC_REGISTER"; break; } snprintf(s_type, sizeof(s_type), "", type); return (s_type); } static const char *st_bindings[] = { "STB_LOCAL", "STB_GLOBAL", "STB_WEAK" }; static char *dynstr; static char *shstrtab; static char *strtab; static FILE *out; static u_int64_t elf_get_byte(Elf32_Ehdr *e, void *base, elf_member_t member); static u_int64_t elf_get_quarter(Elf32_Ehdr *e, void *base, elf_member_t member); #if 0 static u_int64_t elf_get_half(Elf32_Ehdr *e, void *base, elf_member_t member); #endif static u_int64_t elf_get_word(Elf32_Ehdr *e, void *base, elf_member_t member); static u_int64_t elf_get_quad(Elf32_Ehdr *e, void *base, elf_member_t member); static void elf_print_ehdr(Elf32_Ehdr *e, void *sh); static void elf_print_phdr(Elf32_Ehdr *e, void *p); static void elf_print_shdr(Elf32_Ehdr *e, void *sh); static void elf_print_symtab(Elf32_Ehdr *e, void *sh, char *str); static void elf_print_dynamic(Elf32_Ehdr *e, void *sh); static void elf_print_rel(Elf32_Ehdr *e, void *r); static void elf_print_rela(Elf32_Ehdr *e, void *ra); static void elf_print_interp(Elf32_Ehdr *e, void *p); static void elf_print_got(Elf32_Ehdr *e, void *sh); static void elf_print_hash(Elf32_Ehdr *e, void *sh); static void elf_print_note(Elf32_Ehdr *e, void *sh); static void usage(void) __dead2; /* * Helpers for ELF files with shnum or shstrndx values that don't fit in the * ELF header. If the values are too large then an escape value is used to * indicate that the actual value is found in one of section 0's fields. */ static uint64_t elf_get_shnum(Elf32_Ehdr *e, void *sh) { uint64_t shnum; shnum = elf_get_quarter(e, e, E_SHNUM); if (shnum == 0) shnum = elf_get_word(e, (char *)sh, SH_SIZE); return shnum; } static uint64_t elf_get_shstrndx(Elf32_Ehdr *e, void *sh) { uint64_t shstrndx; shstrndx = elf_get_quarter(e, e, E_SHSTRNDX); if (shstrndx == SHN_XINDEX) shstrndx = elf_get_word(e, (char *)sh, SH_LINK); return shstrndx; } int main(int ac, char **av) { cap_rights_t rights; u_int64_t phoff; u_int64_t shoff; u_int64_t phentsize; u_int64_t phnum; u_int64_t shentsize; u_int64_t shnum; u_int64_t shstrndx; u_int64_t offset; u_int64_t name; u_int64_t type; struct stat sb; u_int flags; Elf32_Ehdr *e; void *p; void *sh; void *v; int fd; int ch; int i; out = stdout; flags = 0; while ((ch = getopt(ac, av, "acdEeiGhnprsw:")) != -1) switch (ch) { case 'a': flags = ED_ALL; break; case 'c': flags |= ED_SHDR; break; case 'd': flags |= ED_DYN; break; case 'E': flags = ED_IS_ELF; break; case 'e': flags |= ED_EHDR; break; case 'i': flags |= ED_INTERP; break; case 'G': flags |= ED_GOT; break; case 'h': flags |= ED_HASH; break; case 'n': flags |= ED_NOTE; break; case 'p': flags |= ED_PHDR; break; case 'r': flags |= ED_REL; break; case 's': flags |= ED_SYMTAB; break; case 'w': if ((out = fopen(optarg, "w")) == NULL) err(1, "%s", optarg); cap_rights_init(&rights, CAP_FSTAT, CAP_WRITE); if (caph_rights_limit(fileno(out), &rights) < 0) err(1, "unable to limit rights for %s", optarg); break; case '?': default: usage(); } ac -= optind; av += optind; if (ac == 0 || flags == 0 || ((flags & ED_IS_ELF) && (ac != 1 || (flags & ~ED_IS_ELF) || out != stdout))) usage(); if ((fd = open(*av, O_RDONLY)) < 0 || fstat(fd, &sb) < 0) err(1, "%s", *av); if ((size_t)sb.st_size < sizeof(Elf32_Ehdr)) { if (flags & ED_IS_ELF) exit(1); errx(1, "not an elf file"); } cap_rights_init(&rights, CAP_MMAP_R); if (caph_rights_limit(fd, &rights) < 0) err(1, "unable to limit rights for %s", *av); cap_rights_init(&rights); if (caph_rights_limit(STDIN_FILENO, &rights) < 0 || caph_limit_stdout() < 0 || caph_limit_stderr() < 0) { err(1, "unable to limit rights for stdio"); } if (caph_enter() < 0) err(1, "unable to enter capability mode"); e = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); if (e == MAP_FAILED) err(1, NULL); if (!IS_ELF(*e)) { if (flags & ED_IS_ELF) exit(1); errx(1, "not an elf file"); } else if (flags & ED_IS_ELF) exit (0); phoff = elf_get_off(e, e, E_PHOFF); shoff = elf_get_off(e, e, E_SHOFF); phentsize = elf_get_quarter(e, e, E_PHENTSIZE); phnum = elf_get_quarter(e, e, E_PHNUM); shentsize = elf_get_quarter(e, e, E_SHENTSIZE); p = (char *)e + phoff; if (shoff > 0) { sh = (char *)e + shoff; shnum = elf_get_shnum(e, sh); shstrndx = elf_get_shstrndx(e, sh); offset = elf_get_off(e, (char *)sh + shstrndx * shentsize, SH_OFFSET); shstrtab = (char *)e + offset; } else { sh = NULL; shnum = 0; shstrndx = 0; shstrtab = NULL; } for (i = 0; (u_int64_t)i < shnum; i++) { name = elf_get_word(e, (char *)sh + i * shentsize, SH_NAME); offset = elf_get_off(e, (char *)sh + i * shentsize, SH_OFFSET); if (strcmp(shstrtab + name, ".strtab") == 0) strtab = (char *)e + offset; if (strcmp(shstrtab + name, ".dynstr") == 0) dynstr = (char *)e + offset; } if (flags & ED_EHDR) elf_print_ehdr(e, sh); if (flags & ED_PHDR) elf_print_phdr(e, p); if (flags & ED_SHDR) elf_print_shdr(e, sh); for (i = 0; (u_int64_t)i < phnum; i++) { v = (char *)p + i * phentsize; type = elf_get_word(e, v, P_TYPE); switch (type) { case PT_INTERP: if (flags & ED_INTERP) elf_print_interp(e, v); break; case PT_NULL: case PT_LOAD: case PT_DYNAMIC: case PT_NOTE: case PT_SHLIB: case PT_PHDR: break; } } for (i = 0; (u_int64_t)i < shnum; i++) { v = (char *)sh + i * shentsize; type = elf_get_word(e, v, SH_TYPE); switch (type) { case SHT_SYMTAB: if (flags & ED_SYMTAB) elf_print_symtab(e, v, strtab); break; case SHT_DYNAMIC: if (flags & ED_DYN) elf_print_dynamic(e, v); break; case SHT_RELA: if (flags & ED_REL) elf_print_rela(e, v); break; case SHT_REL: if (flags & ED_REL) elf_print_rel(e, v); break; case SHT_NOTE: name = elf_get_word(e, v, SH_NAME); if (flags & ED_NOTE && strcmp(shstrtab + name, ".note.tag") == 0) elf_print_note(e, v); break; case SHT_DYNSYM: if (flags & ED_SYMTAB) elf_print_symtab(e, v, dynstr); break; case SHT_PROGBITS: name = elf_get_word(e, v, SH_NAME); if (flags & ED_GOT && strcmp(shstrtab + name, ".got") == 0) elf_print_got(e, v); break; case SHT_HASH: if (flags & ED_HASH) elf_print_hash(e, v); break; case SHT_NULL: case SHT_STRTAB: case SHT_NOBITS: case SHT_SHLIB: break; } } return 0; } static void elf_print_ehdr(Elf32_Ehdr *e, void *sh) { u_int64_t class; u_int64_t data; u_int64_t osabi; u_int64_t type; u_int64_t machine; u_int64_t version; u_int64_t entry; u_int64_t phoff; u_int64_t shoff; u_int64_t flags; u_int64_t ehsize; u_int64_t phentsize; u_int64_t phnum; u_int64_t shentsize; u_int64_t shnum; u_int64_t shstrndx; class = elf_get_byte(e, e, E_CLASS); data = elf_get_byte(e, e, E_DATA); osabi = elf_get_byte(e, e, E_OSABI); type = elf_get_quarter(e, e, E_TYPE); machine = elf_get_quarter(e, e, E_MACHINE); version = elf_get_word(e, e, E_VERSION); entry = elf_get_addr(e, e, E_ENTRY); phoff = elf_get_off(e, e, E_PHOFF); shoff = elf_get_off(e, e, E_SHOFF); flags = elf_get_word(e, e, E_FLAGS); ehsize = elf_get_quarter(e, e, E_EHSIZE); phentsize = elf_get_quarter(e, e, E_PHENTSIZE); phnum = elf_get_quarter(e, e, E_PHNUM); shentsize = elf_get_quarter(e, e, E_SHENTSIZE); fprintf(out, "\nelf header:\n"); fprintf(out, "\n"); fprintf(out, "\te_ident: %s %s %s\n", ei_classes[class], ei_data[data], ei_abis[osabi]); fprintf(out, "\te_type: %s\n", e_types[type]); fprintf(out, "\te_machine: %s\n", e_machines(machine)); fprintf(out, "\te_version: %s\n", ei_versions[version]); fprintf(out, "\te_entry: %#jx\n", (intmax_t)entry); fprintf(out, "\te_phoff: %jd\n", (intmax_t)phoff); fprintf(out, "\te_shoff: %jd\n", (intmax_t)shoff); fprintf(out, "\te_flags: %jd\n", (intmax_t)flags); fprintf(out, "\te_ehsize: %jd\n", (intmax_t)ehsize); fprintf(out, "\te_phentsize: %jd\n", (intmax_t)phentsize); fprintf(out, "\te_phnum: %jd\n", (intmax_t)phnum); fprintf(out, "\te_shentsize: %jd\n", (intmax_t)shentsize); if (sh != NULL) { shnum = elf_get_shnum(e, sh); shstrndx = elf_get_shstrndx(e, sh); fprintf(out, "\te_shnum: %jd\n", (intmax_t)shnum); fprintf(out, "\te_shstrndx: %jd\n", (intmax_t)shstrndx); } } static void elf_print_phdr(Elf32_Ehdr *e, void *p) { u_int64_t phentsize; u_int64_t phnum; u_int64_t type; u_int64_t offset; u_int64_t vaddr; u_int64_t paddr; u_int64_t filesz; u_int64_t memsz; u_int64_t flags; u_int64_t align; void *v; int i; phentsize = elf_get_quarter(e, e, E_PHENTSIZE); phnum = elf_get_quarter(e, e, E_PHNUM); fprintf(out, "\nprogram header:\n"); for (i = 0; (u_int64_t)i < phnum; i++) { v = (char *)p + i * phentsize; type = elf_get_word(e, v, P_TYPE); offset = elf_get_off(e, v, P_OFFSET); vaddr = elf_get_addr(e, v, P_VADDR); paddr = elf_get_addr(e, v, P_PADDR); filesz = elf_get_size(e, v, P_FILESZ); memsz = elf_get_size(e, v, P_MEMSZ); flags = elf_get_word(e, v, P_FLAGS); align = elf_get_size(e, v, P_ALIGN); fprintf(out, "\n"); fprintf(out, "entry: %d\n", i); fprintf(out, "\tp_type: %s\n", p_types[type & 0x7]); fprintf(out, "\tp_offset: %jd\n", (intmax_t)offset); fprintf(out, "\tp_vaddr: %#jx\n", (intmax_t)vaddr); fprintf(out, "\tp_paddr: %#jx\n", (intmax_t)paddr); fprintf(out, "\tp_filesz: %jd\n", (intmax_t)filesz); fprintf(out, "\tp_memsz: %jd\n", (intmax_t)memsz); fprintf(out, "\tp_flags: %s\n", p_flags[flags]); fprintf(out, "\tp_align: %jd\n", (intmax_t)align); } } static void elf_print_shdr(Elf32_Ehdr *e, void *sh) { u_int64_t shentsize; u_int64_t shnum; u_int64_t name; u_int64_t type; u_int64_t flags; u_int64_t addr; u_int64_t offset; u_int64_t size; u_int64_t shlink; u_int64_t info; u_int64_t addralign; u_int64_t entsize; u_int64_t machine; void *v; int i; if (sh == NULL) { fprintf(out, "\nNo section headers\n"); return; } machine = elf_get_quarter(e, e, E_MACHINE); shentsize = elf_get_quarter(e, e, E_SHENTSIZE); shnum = elf_get_shnum(e, sh); fprintf(out, "\nsection header:\n"); for (i = 0; (u_int64_t)i < shnum; i++) { v = (char *)sh + i * shentsize; name = elf_get_word(e, v, SH_NAME); type = elf_get_word(e, v, SH_TYPE); flags = elf_get_word(e, v, SH_FLAGS); addr = elf_get_addr(e, v, SH_ADDR); offset = elf_get_off(e, v, SH_OFFSET); size = elf_get_size(e, v, SH_SIZE); shlink = elf_get_word(e, v, SH_LINK); info = elf_get_word(e, v, SH_INFO); addralign = elf_get_size(e, v, SH_ADDRALIGN); entsize = elf_get_size(e, v, SH_ENTSIZE); fprintf(out, "\n"); fprintf(out, "entry: %d\n", i); fprintf(out, "\tsh_name: %s\n", shstrtab + name); fprintf(out, "\tsh_type: %s\n", sh_types(machine, type)); fprintf(out, "\tsh_flags: %s\n", sh_flags[flags & 0x7]); fprintf(out, "\tsh_addr: %#jx\n", addr); fprintf(out, "\tsh_offset: %jd\n", (intmax_t)offset); fprintf(out, "\tsh_size: %jd\n", (intmax_t)size); fprintf(out, "\tsh_link: %jd\n", (intmax_t)shlink); fprintf(out, "\tsh_info: %jd\n", (intmax_t)info); fprintf(out, "\tsh_addralign: %jd\n", (intmax_t)addralign); fprintf(out, "\tsh_entsize: %jd\n", (intmax_t)entsize); } } static void elf_print_symtab(Elf32_Ehdr *e, void *sh, char *str) { u_int64_t machine; u_int64_t offset; u_int64_t entsize; u_int64_t size; u_int64_t name; u_int64_t value; u_int64_t info; u_int64_t shndx; void *st; int len; int i; machine = elf_get_quarter(e, e, E_MACHINE); offset = elf_get_off(e, sh, SH_OFFSET); entsize = elf_get_size(e, sh, SH_ENTSIZE); size = elf_get_size(e, sh, SH_SIZE); name = elf_get_word(e, sh, SH_NAME); len = size / entsize; fprintf(out, "\nsymbol table (%s):\n", shstrtab + name); for (i = 0; i < len; i++) { st = (char *)e + offset + i * entsize; name = elf_get_word(e, st, ST_NAME); value = elf_get_addr(e, st, ST_VALUE); size = elf_get_size(e, st, ST_SIZE); info = elf_get_byte(e, st, ST_INFO); shndx = elf_get_quarter(e, st, ST_SHNDX); fprintf(out, "\n"); fprintf(out, "entry: %d\n", i); fprintf(out, "\tst_name: %s\n", str + name); fprintf(out, "\tst_value: %#jx\n", value); fprintf(out, "\tst_size: %jd\n", (intmax_t)size); fprintf(out, "\tst_info: %s %s\n", st_type(machine, ELF32_ST_TYPE(info)), st_bindings[ELF32_ST_BIND(info)]); fprintf(out, "\tst_shndx: %jd\n", (intmax_t)shndx); } } static void elf_print_dynamic(Elf32_Ehdr *e, void *sh) { u_int64_t offset; u_int64_t entsize; u_int64_t size; int64_t tag; u_int64_t ptr; u_int64_t val; void *d; int i; offset = elf_get_off(e, sh, SH_OFFSET); entsize = elf_get_size(e, sh, SH_ENTSIZE); size = elf_get_size(e, sh, SH_SIZE); fprintf(out, "\ndynamic:\n"); for (i = 0; (u_int64_t)i < size / entsize; i++) { d = (char *)e + offset + i * entsize; tag = elf_get_size(e, d, D_TAG); ptr = elf_get_size(e, d, D_PTR); val = elf_get_addr(e, d, D_VAL); fprintf(out, "\n"); fprintf(out, "entry: %d\n", i); fprintf(out, "\td_tag: %s\n", d_tags(tag)); switch (tag) { case DT_NEEDED: case DT_SONAME: case DT_RPATH: fprintf(out, "\td_val: %s\n", dynstr + val); break; case DT_PLTRELSZ: case DT_RELA: case DT_RELASZ: case DT_RELAENT: case DT_STRSZ: case DT_SYMENT: case DT_RELSZ: case DT_RELENT: case DT_PLTREL: fprintf(out, "\td_val: %jd\n", (intmax_t)val); break; case DT_PLTGOT: case DT_HASH: case DT_STRTAB: case DT_SYMTAB: case DT_INIT: case DT_FINI: case DT_REL: case DT_JMPREL: fprintf(out, "\td_ptr: %#jx\n", ptr); break; case DT_NULL: case DT_SYMBOLIC: case DT_DEBUG: case DT_TEXTREL: break; } } } static void elf_print_rela(Elf32_Ehdr *e, void *sh) { u_int64_t offset; u_int64_t entsize; u_int64_t size; u_int64_t name; u_int64_t info; int64_t addend; void *ra; void *v; int i; offset = elf_get_off(e, sh, SH_OFFSET); entsize = elf_get_size(e, sh, SH_ENTSIZE); size = elf_get_size(e, sh, SH_SIZE); name = elf_get_word(e, sh, SH_NAME); v = (char *)e + offset; fprintf(out, "\nrelocation with addend (%s):\n", shstrtab + name); for (i = 0; (u_int64_t)i < size / entsize; i++) { ra = (char *)v + i * entsize; offset = elf_get_addr(e, ra, RA_OFFSET); info = elf_get_word(e, ra, RA_INFO); addend = elf_get_off(e, ra, RA_ADDEND); fprintf(out, "\n"); fprintf(out, "entry: %d\n", i); fprintf(out, "\tr_offset: %#jx\n", offset); fprintf(out, "\tr_info: %jd\n", (intmax_t)info); fprintf(out, "\tr_addend: %jd\n", (intmax_t)addend); } } static void elf_print_rel(Elf32_Ehdr *e, void *sh) { u_int64_t offset; u_int64_t entsize; u_int64_t size; u_int64_t name; u_int64_t info; void *r; void *v; int i; offset = elf_get_off(e, sh, SH_OFFSET); entsize = elf_get_size(e, sh, SH_ENTSIZE); size = elf_get_size(e, sh, SH_SIZE); name = elf_get_word(e, sh, SH_NAME); v = (char *)e + offset; fprintf(out, "\nrelocation (%s):\n", shstrtab + name); for (i = 0; (u_int64_t)i < size / entsize; i++) { r = (char *)v + i * entsize; offset = elf_get_addr(e, r, R_OFFSET); info = elf_get_word(e, r, R_INFO); fprintf(out, "\n"); fprintf(out, "entry: %d\n", i); fprintf(out, "\tr_offset: %#jx\n", offset); fprintf(out, "\tr_info: %jd\n", (intmax_t)info); } } static void elf_print_interp(Elf32_Ehdr *e, void *p) { u_int64_t offset; char *s; offset = elf_get_off(e, p, P_OFFSET); s = (char *)e + offset; fprintf(out, "\ninterp:\n"); fprintf(out, "\t%s\n", s); } static void elf_print_got(Elf32_Ehdr *e, void *sh) { u_int64_t offset; u_int64_t addralign; u_int64_t size; u_int64_t addr; void *v; int i; offset = elf_get_off(e, sh, SH_OFFSET); addralign = elf_get_size(e, sh, SH_ADDRALIGN); size = elf_get_size(e, sh, SH_SIZE); v = (char *)e + offset; fprintf(out, "\nglobal offset table:\n"); for (i = 0; (u_int64_t)i < size / addralign; i++) { addr = elf_get_addr(e, (char *)v + i * addralign, 0); fprintf(out, "\n"); fprintf(out, "entry: %d\n", i); fprintf(out, "\t%#jx\n", addr); } } static void elf_print_hash(Elf32_Ehdr *e __unused, void *sh __unused) { } static void elf_print_note(Elf32_Ehdr *e, void *sh) { u_int64_t offset; u_int64_t size; u_int64_t name; u_int32_t namesz; u_int32_t descsz; u_int32_t desc; u_int32_t type; char *n, *s; const char *nt_type; offset = elf_get_off(e, sh, SH_OFFSET); size = elf_get_size(e, sh, SH_SIZE); name = elf_get_word(e, sh, SH_NAME); n = (char *)e + offset; fprintf(out, "\nnote (%s):\n", shstrtab + name); while (n < ((char *)e + offset + size)) { namesz = elf_get_word(e, n, N_NAMESZ); descsz = elf_get_word(e, n, N_DESCSZ); type = elf_get_word(e, n, N_TYPE); if (type < nitems(nt_types) && nt_types[type] != NULL) nt_type = nt_types[type]; else nt_type = "Unknown type"; s = n + sizeof(Elf_Note); desc = elf_get_word(e, n + sizeof(Elf_Note) + namesz, 0); fprintf(out, "\t%s %d (%s)\n", s, desc, nt_type); n += sizeof(Elf_Note) + namesz + descsz; } } static u_int64_t elf_get_byte(Elf32_Ehdr *e, void *base, elf_member_t member) { u_int64_t val; val = 0; switch (e->e_ident[EI_CLASS]) { case ELFCLASS32: val = ((uint8_t *)base)[elf32_offsets[member]]; break; case ELFCLASS64: val = ((uint8_t *)base)[elf64_offsets[member]]; break; case ELFCLASSNONE: errx(1, "invalid class"); } return val; } static u_int64_t elf_get_quarter(Elf32_Ehdr *e, void *base, elf_member_t member) { u_int64_t val; val = 0; switch (e->e_ident[EI_CLASS]) { case ELFCLASS32: base = (char *)base + elf32_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be16dec(base); break; case ELFDATA2LSB: val = le16dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASS64: base = (char *)base + elf64_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be16dec(base); break; case ELFDATA2LSB: val = le16dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASSNONE: errx(1, "invalid class"); } return val; } #if 0 static u_int64_t elf_get_half(Elf32_Ehdr *e, void *base, elf_member_t member) { u_int64_t val; val = 0; switch (e->e_ident[EI_CLASS]) { case ELFCLASS32: base = (char *)base + elf32_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be16dec(base); break; case ELFDATA2LSB: val = le16dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASS64: base = (char *)base + elf64_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be32dec(base); break; case ELFDATA2LSB: val = le32dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASSNONE: errx(1, "invalid class"); } return val; } #endif static u_int64_t elf_get_word(Elf32_Ehdr *e, void *base, elf_member_t member) { u_int64_t val; val = 0; switch (e->e_ident[EI_CLASS]) { case ELFCLASS32: base = (char *)base + elf32_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be32dec(base); break; case ELFDATA2LSB: val = le32dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASS64: base = (char *)base + elf64_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be32dec(base); break; case ELFDATA2LSB: val = le32dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASSNONE: errx(1, "invalid class"); } return val; } static u_int64_t elf_get_quad(Elf32_Ehdr *e, void *base, elf_member_t member) { u_int64_t val; val = 0; switch (e->e_ident[EI_CLASS]) { case ELFCLASS32: base = (char *)base + elf32_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be32dec(base); break; case ELFDATA2LSB: val = le32dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASS64: base = (char *)base + elf64_offsets[member]; switch (e->e_ident[EI_DATA]) { case ELFDATA2MSB: val = be64dec(base); break; case ELFDATA2LSB: val = le64dec(base); break; case ELFDATANONE: errx(1, "invalid data format"); } break; case ELFCLASSNONE: errx(1, "invalid class"); } return val; } static void usage(void) { fprintf(stderr, "usage: elfdump -a | -E | -cdeGhinprs [-w file] file\n"); exit(1); } diff --git a/usr.bin/enigma/enigma.c b/usr.bin/enigma/enigma.c index 32f8972b238a..b985163476c4 100644 --- a/usr.bin/enigma/enigma.c +++ b/usr.bin/enigma/enigma.c @@ -1,155 +1,154 @@ /*- * "enigma.c" is in file cbw.tar from * anonymous FTP host watmsg.waterloo.edu: pub/crypt/cbw.tar.Z * * A one-rotor machine designed along the lines of Enigma * but considerably trivialized. * * A public-domain replacement for the UNIX "crypt" command. * * Upgraded to function properly on 64-bit machines. */ -#include #include #include #include #include #include #define MINUSKVAR "CrYpTkEy" #define ROTORSZ 256 #define MASK 0377 static char t1[ROTORSZ]; static char t2[ROTORSZ]; static char t3[ROTORSZ]; static char deck[ROTORSZ]; static char buf[13]; static void shuffle(char *); static void setup(char *); static void setup(char *pw) { int ic, i, k, temp; char salt[3]; unsigned rnd; int32_t seed; char *cryptpw; if (crypt_set_format("des") == 0) { fprintf(stderr, "crypt_set_format(\"des\") failed.\n"); exit(1); } strlcpy(salt, pw, sizeof(salt)); cryptpw = crypt(pw, salt); if (cryptpw == NULL) { fprintf(stderr, "crypt(3) failure\n"); exit(1); } memcpy(buf, cryptpw, sizeof(buf)); seed = 123; for (i=0; i<13; i++) seed = seed*buf[i] + i; for(i=0;i>= 8; temp = t1[k]; t1[k] = t1[ic]; t1[ic] = temp; if(t3[k]!=0) continue; ic = (rnd&MASK) % k; while(t3[ic]!=0) ic = (ic+1) % k; t3[k] = ic; t3[ic] = k; } for(i=0;i 1 && argv[1][0] == '-') { if (argv[1][1] == 's') { argc--; argv++; secureflg = 1; } else if (argv[1][1] == 'k') { argc--; argv++; kflag = 1; } } if (kflag) { if ((cp = getenv(MINUSKVAR)) == NULL) { fprintf(stderr, "%s not set\n", MINUSKVAR); exit(1); } setup(cp); } else if (argc != 2) { setup(getpass("Enter key:")); } else setup(argv[1]); n1 = 0; n2 = 0; nr2 = 0; while((i=getchar()) != -1) { if (secureflg) { nr1 = deck[n1]&MASK; nr2 = deck[nr1]&MASK; } else { nr1 = n1; } i = t2[(t3[(t1[(i+nr1)&MASK]+nr2)&MASK]-nr2)&MASK]-nr1; putchar(i); n1++; if(n1==ROTORSZ) { n1 = 0; n2++; if(n2==ROTORSZ) n2 = 0; if (secureflg) { shuffle(deck); } else { nr2 = n2; } } } return 0; } static void shuffle(char deckary[]) { int i, ic, k, temp; unsigned rnd; static int32_t seed = 123; for(i=0;i * 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 * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MINBUFSIZE 16384 #define TIMEOUT 120 /* Option flags */ static int A_flag; /* -A: do not follow 302 redirects */ static int a_flag; /* -a: auto retry */ static off_t B_size; /* -B: buffer size */ static int b_flag; /*! -b: workaround TCP bug */ static char *c_dirname; /* -c: remote directory */ static int d_flag; /* -d: direct connection */ static int F_flag; /* -F: restart without checking mtime */ static char *f_filename; /* -f: file to fetch */ static char *h_hostname; /* -h: host to fetch from */ static int i_flag; /* -i: specify file for mtime comparison */ static char *i_filename; /* name of input file */ static int l_flag; /* -l: link rather than copy file: URLs */ static int m_flag; /* -[Mm]: mirror mode */ static char *N_filename; /* -N: netrc file name */ static int n_flag; /* -n: do not preserve modification time */ static int o_flag; /* -o: specify output file */ static int o_directory; /* output file is a directory */ static char *o_filename; /* name of output file */ static int o_stdout; /* output file is stdout */ static int once_flag; /* -1: stop at first successful file */ static int p_flag; /* -[Pp]: use passive FTP */ static int R_flag; /* -R: don't delete partial files */ static int r_flag; /* -r: restart previous transfer */ static off_t S_size; /* -S: require size to match */ static int s_flag; /* -s: show size, don't fetch */ static long T_secs; /* -T: transfer timeout in seconds */ static int t_flag; /*! -t: workaround TCP bug */ static int U_flag; /* -U: do not use high ports */ static int v_level = 1; /* -v: verbosity level */ static int v_tty; /* stdout is a tty */ static int v_progress; /* whether to display progress */ static pid_t pgrp; /* our process group */ static long w_secs; /* -w: retry delay */ static int family = PF_UNSPEC; /* -[46]: address family to use */ static int sigalrm; /* SIGALRM received */ static int siginfo; /* SIGINFO received */ static int sigint; /* SIGINT received */ static long ftp_timeout = TIMEOUT; /* default timeout for FTP transfers */ static long http_timeout = TIMEOUT;/* default timeout for HTTP transfers */ static char *buf; /* transfer buffer */ enum options { OPTION_BIND_ADDRESS, OPTION_NO_FTP_PASSIVE_MODE, OPTION_HTTP_REFERER, OPTION_HTTP_USER_AGENT, OPTION_NO_PROXY, OPTION_SSL_CA_CERT_FILE, OPTION_SSL_CA_CERT_PATH, OPTION_SSL_CLIENT_CERT_FILE, OPTION_SSL_CLIENT_KEY_FILE, OPTION_SSL_CRL_FILE, OPTION_SSL_NO_SSL3, OPTION_SSL_NO_TLS1, OPTION_SSL_NO_VERIFY_HOSTNAME, OPTION_SSL_NO_VERIFY_PEER }; static struct option longopts[] = { /* mapping to single character argument */ { "one-file", no_argument, NULL, '1' }, { "ipv4-only", no_argument, NULL, '4' }, { "ipv6-only", no_argument, NULL, '6' }, { "no-redirect", no_argument, NULL, 'A' }, { "retry", no_argument, NULL, 'a' }, { "buffer-size", required_argument, NULL, 'B' }, /* -c not mapped, since it's deprecated */ { "direct", no_argument, NULL, 'd' }, { "force-restart", no_argument, NULL, 'F' }, /* -f not mapped, since it's deprecated */ /* -h not mapped, since it's deprecated */ { "if-modified-since", required_argument, NULL, 'i' }, { "symlink", no_argument, NULL, 'l' }, /* -M not mapped since it's the same as -m */ { "mirror", no_argument, NULL, 'm' }, { "netrc", required_argument, NULL, 'N' }, { "no-mtime", no_argument, NULL, 'n' }, { "output", required_argument, NULL, 'o' }, /* -P not mapped since it's the same as -p */ { "passive", no_argument, NULL, 'p' }, { "quiet", no_argument, NULL, 'q' }, { "keep-output", no_argument, NULL, 'R' }, { "restart", no_argument, NULL, 'r' }, { "require-size", required_argument, NULL, 'S' }, { "print-size", no_argument, NULL, 's' }, { "timeout", required_argument, NULL, 'T' }, { "passive-portrange-default", no_argument, NULL, 'T' }, { "verbose", no_argument, NULL, 'v' }, { "retry-delay", required_argument, NULL, 'w' }, /* options without a single character equivalent */ { "bind-address", required_argument, NULL, OPTION_BIND_ADDRESS }, { "no-passive", no_argument, NULL, OPTION_NO_FTP_PASSIVE_MODE }, { "referer", required_argument, NULL, OPTION_HTTP_REFERER }, { "user-agent", required_argument, NULL, OPTION_HTTP_USER_AGENT }, { "no-proxy", required_argument, NULL, OPTION_NO_PROXY }, { "ca-cert", required_argument, NULL, OPTION_SSL_CA_CERT_FILE }, { "ca-path", required_argument, NULL, OPTION_SSL_CA_CERT_PATH }, { "cert", required_argument, NULL, OPTION_SSL_CLIENT_CERT_FILE }, { "key", required_argument, NULL, OPTION_SSL_CLIENT_KEY_FILE }, { "crl", required_argument, NULL, OPTION_SSL_CRL_FILE }, { "no-sslv3", no_argument, NULL, OPTION_SSL_NO_SSL3 }, { "no-tlsv1", no_argument, NULL, OPTION_SSL_NO_TLS1 }, { "no-verify-hostname", no_argument, NULL, OPTION_SSL_NO_VERIFY_HOSTNAME }, { "no-verify-peer", no_argument, NULL, OPTION_SSL_NO_VERIFY_PEER }, { NULL, 0, NULL, 0 } }; /* * Signal handler */ static void sig_handler(int sig) { switch (sig) { case SIGALRM: sigalrm = 1; break; case SIGINFO: siginfo = 1; break; case SIGINT: sigint = 1; break; } } struct xferstat { char name[64]; struct timeval start; /* start of transfer */ struct timeval last; /* time of last update */ struct timeval last2; /* time of previous last update */ off_t size; /* size of file per HTTP hdr */ off_t offset; /* starting offset in file */ off_t rcvd; /* bytes already received */ off_t lastrcvd; /* bytes received since last update */ }; /* * Format a number of seconds as either XXdYYh, XXhYYm, XXmYYs, or XXs * depending on its magnitude */ static void stat_seconds(char *str, size_t strsz, long seconds) { if (seconds > 86400) snprintf(str, strsz, "%02ldd%02ldh", seconds / 86400, (seconds % 86400) / 3600); else if (seconds > 3600) snprintf(str, strsz, "%02ldh%02ldm", seconds / 3600, (seconds % 3600) / 60); else if (seconds > 60) snprintf(str, strsz, "%02ldm%02lds", seconds / 60, seconds % 60); else snprintf(str, strsz, " %02lds", seconds); } /* * Compute and display ETA */ static void stat_eta(char *str, size_t strsz, const struct xferstat *xs) { long elapsed, eta; off_t received, expected; elapsed = xs->last.tv_sec - xs->start.tv_sec; received = xs->rcvd - xs->offset; expected = xs->size - xs->rcvd; eta = (long)((double)elapsed * expected / received); if (eta > 0) stat_seconds(str, strsz, eta); else stat_seconds(str, strsz, elapsed); } /* * Format a number as "xxxx YB" where Y is ' ', 'k', 'M'... */ static const char *prefixes = " kMGTP"; static void stat_bytes(char *str, size_t strsz, off_t bytes) { const char *prefix = prefixes; while (bytes > 9999 && prefix[1] != '\0') { bytes /= 1024; prefix++; } snprintf(str, strsz, "%4ju %cB", (uintmax_t)bytes, *prefix); } /* * Compute and display transfer rate */ static void stat_bps(char *str, size_t strsz, struct xferstat *xs) { char bytes[16]; double delta, bps; delta = ((double)xs->last.tv_sec + (xs->last.tv_usec / 1.e6)) - ((double)xs->last2.tv_sec + (xs->last2.tv_usec / 1.e6)); if (delta == 0.0) { snprintf(str, strsz, "?? Bps"); } else { bps = (xs->rcvd - xs->lastrcvd) / delta; stat_bytes(bytes, sizeof bytes, (off_t)bps); snprintf(str, strsz, "%sps", bytes); } } /* * Update the stats display */ static void stat_display(struct xferstat *xs, int force) { char bytes[16], bps[16], eta[16]; struct timeval now; int ctty_pgrp; /* check if we're the foreground process */ if (ioctl(STDERR_FILENO, TIOCGPGRP, &ctty_pgrp) != 0 || (pid_t)ctty_pgrp != pgrp) return; gettimeofday(&now, NULL); if (!force && now.tv_sec <= xs->last.tv_sec) return; xs->last2 = xs->last; xs->last = now; fprintf(stderr, "\r%-46.46s", xs->name); if (xs->rcvd >= xs->size) { stat_bytes(bytes, sizeof bytes, xs->rcvd); setproctitle("%s [%s]", xs->name, bytes); fprintf(stderr, " %s", bytes); } else { stat_bytes(bytes, sizeof bytes, xs->size); setproctitle("%s [%d%% of %s]", xs->name, (int)((100.0 * xs->rcvd) / xs->size), bytes); fprintf(stderr, "%3d%% of %s", (int)((100.0 * xs->rcvd) / xs->size), bytes); } if (force == 2) { xs->lastrcvd = xs->offset; xs->last2 = xs->start; } stat_bps(bps, sizeof bps, xs); fprintf(stderr, " %s", bps); if ((xs->size > 0 && xs->rcvd > 0 && xs->last.tv_sec >= xs->start.tv_sec + 3) || force == 2) { stat_eta(eta, sizeof eta, xs); fprintf(stderr, " %s", eta); } xs->lastrcvd = xs->rcvd; } /* * Initialize the transfer statistics */ static void stat_start(struct xferstat *xs, const char *name, off_t size, off_t offset) { memset(xs, 0, sizeof *xs); snprintf(xs->name, sizeof xs->name, "%s", name); gettimeofday(&xs->start, NULL); xs->last2 = xs->last = xs->start; xs->size = size; xs->offset = offset; xs->rcvd = offset; xs->lastrcvd = offset; if (v_progress) stat_display(xs, 1); else if (v_level > 0) fprintf(stderr, "%-46s", xs->name); } /* * Update the transfer statistics */ static void stat_update(struct xferstat *xs, off_t rcvd) { xs->rcvd = rcvd; if (v_progress) stat_display(xs, 0); } /* * Finalize the transfer statistics */ static void stat_end(struct xferstat *xs) { char bytes[16], bps[16], eta[16]; gettimeofday(&xs->last, NULL); if (v_progress) { stat_display(xs, 2); putc('\n', stderr); } else if (v_level > 0) { stat_bytes(bytes, sizeof bytes, xs->rcvd); stat_bps(bps, sizeof bps, xs); stat_eta(eta, sizeof eta, xs); fprintf(stderr, " %s %s %s\n", bytes, bps, eta); } } /* * Ask the user for authentication details */ static int query_auth(struct url *URL) { struct termios tios; tcflag_t saved_flags; int i, nopwd; fprintf(stderr, "Authentication required for <%s://%s:%d/>!\n", URL->scheme, URL->host, URL->port); fprintf(stderr, "Login: "); if (fgets(URL->user, sizeof URL->user, stdin) == NULL) return (-1); for (i = strlen(URL->user); i >= 0; --i) if (URL->user[i] == '\r' || URL->user[i] == '\n') URL->user[i] = '\0'; fprintf(stderr, "Password: "); if (tcgetattr(STDIN_FILENO, &tios) == 0) { saved_flags = tios.c_lflag; tios.c_lflag &= ~ECHO; tios.c_lflag |= ECHONL|ICANON; tcsetattr(STDIN_FILENO, TCSAFLUSH|TCSASOFT, &tios); nopwd = (fgets(URL->pwd, sizeof URL->pwd, stdin) == NULL); tios.c_lflag = saved_flags; tcsetattr(STDIN_FILENO, TCSANOW|TCSASOFT, &tios); } else { nopwd = (fgets(URL->pwd, sizeof URL->pwd, stdin) == NULL); } if (nopwd) return (-1); for (i = strlen(URL->pwd); i >= 0; --i) if (URL->pwd[i] == '\r' || URL->pwd[i] == '\n') URL->pwd[i] = '\0'; return (0); } /* * Fetch a file */ static int fetch(char *URL, const char *path, int *is_http) { struct url *url; struct url_stat us; struct stat sb, nsb; struct xferstat xs; FILE *f, *of; size_t size, readcnt, wr; off_t count, size_prev; char flags[8]; const char *slash; char *tmppath; int r, tries; unsigned timeout; char *ptr; f = of = NULL; tmppath = NULL; timeout = 0; *flags = 0; count = 0; /* set verbosity level */ if (v_level > 1) strcat(flags, "v"); if (v_level > 2) fetchDebug = 1; /* parse URL */ url = NULL; if (*URL == '\0') { warnx("empty URL"); goto failure; } if ((url = fetchParseURL(URL)) == NULL) { warnx("%s: parse error", URL); goto failure; } /* if no scheme was specified, take a guess */ if (!*url->scheme) { if (!*url->host) strcpy(url->scheme, SCHEME_FILE); else if (strncasecmp(url->host, "ftp.", 4) == 0) strcpy(url->scheme, SCHEME_FTP); else if (strncasecmp(url->host, "www.", 4) == 0) strcpy(url->scheme, SCHEME_HTTP); } /* for both of http and https */ *is_http = strncmp(url->scheme, "http", 4) == 0; /* common flags */ switch (family) { case PF_INET: strcat(flags, "4"); break; case PF_INET6: strcat(flags, "6"); break; } /* FTP specific flags */ if (strcmp(url->scheme, SCHEME_FTP) == 0) { if (p_flag) strcat(flags, "p"); if (d_flag) strcat(flags, "d"); if (U_flag) strcat(flags, "l"); timeout = T_secs ? T_secs : ftp_timeout; } /* HTTP specific flags */ if (strcmp(url->scheme, SCHEME_HTTP) == 0 || strcmp(url->scheme, SCHEME_HTTPS) == 0) { if (d_flag) strcat(flags, "d"); if (A_flag) strcat(flags, "A"); timeout = T_secs ? T_secs : http_timeout; if (i_flag) { if (stat(i_filename, &sb)) { warn("%s: stat()", i_filename); goto failure; } url->ims_time = sb.st_mtime; strcat(flags, "i"); } } /* set the protocol timeout. */ fetchTimeout = timeout; /* just print size */ if (s_flag) { if (timeout) alarm(timeout); r = fetchStat(url, &us, flags); if (timeout) alarm(0); if (sigalrm || sigint) goto signal; if (r == -1) { warnx("%s", fetchLastErrString); goto failure; } if (us.size == -1) printf("Unknown\n"); else printf("%jd\n", (intmax_t)us.size); goto success; } tries = 1; again: r = 0; /* * If the -r flag was specified, we have to compare the local * and remote files, so we should really do a fetchStat() * first, but I know of at least one HTTP server that only * sends the content size in response to GET requests, and * leaves it out of replies to HEAD requests. Also, in the * (frequent) case that the local and remote files match but * the local file is truncated, we have sufficient information * before the compare to issue a correct request. Therefore, * we always issue a GET request as if we were sure the local * file was a truncated copy of the remote file; we can drop * the connection later if we change our minds. */ sb.st_size = -1; if (!o_stdout) { r = stat(path, &sb); if (r == 0 && (r_flag || tries > 1) && S_ISREG(sb.st_mode)) { url->offset = sb.st_size; } else if (r == -1 || !S_ISREG(sb.st_mode)) { /* * Whatever value sb.st_size has now is either * wrong (if stat(2) failed) or irrelevant (if the * path does not refer to a regular file) */ sb.st_size = -1; } if (r == -1 && errno != ENOENT) { warnx("%s: stat()", path); goto failure; } } size_prev = sb.st_size; /* start the transfer */ if (timeout) alarm(timeout); f = fetchXGet(url, &us, flags); if (timeout) alarm(0); if (sigalrm || sigint) goto signal; if (f == NULL) { warnx("%s: %s", URL, fetchLastErrString); if (i_flag && (strcmp(url->scheme, SCHEME_HTTP) == 0 || strcmp(url->scheme, SCHEME_HTTPS) == 0) && fetchLastErrCode == FETCH_OK && strcmp(fetchLastErrString, "Not Modified") == 0) { /* HTTP Not Modified Response, return OK. */ r = 0; goto done; } else goto failure; } if (sigint) goto signal; /* check that size is as expected */ if (S_size) { if (us.size == -1) { warnx("%s: size unknown", URL); } else if (us.size != S_size) { warnx("%s: size mismatch: expected %jd, actual %jd", URL, (intmax_t)S_size, (intmax_t)us.size); goto failure; } } /* symlink instead of copy */ if (l_flag && strcmp(url->scheme, "file") == 0 && !o_stdout) { if (symlink(url->doc, path) == -1) { warn("%s: symlink()", path); goto failure; } goto success; } if (us.size == -1 && !o_stdout && v_level > 0) warnx("%s: size of remote file is not known", URL); if (v_level > 1) { if (sb.st_size != -1) fprintf(stderr, "local size / mtime: %jd / %ld\n", (intmax_t)sb.st_size, (long)sb.st_mtime); if (us.size != -1) fprintf(stderr, "remote size / mtime: %jd / %ld\n", (intmax_t)us.size, (long)us.mtime); } /* open output file */ if (o_stdout) { /* output to stdout */ of = stdout; } else if (r_flag && sb.st_size != -1) { /* resume mode, local file exists */ if (!F_flag && us.mtime && sb.st_mtime != us.mtime && tries == 1) { /* no match! have to refetch */ fclose(f); /* if precious, warn the user and give up */ if (R_flag) { warnx("%s: local modification time " "does not match remote", path); goto failure_keep; } } else if (url->offset > sb.st_size) { /* gap between what we asked for and what we got */ warnx("%s: gap in resume mode", URL); fclose(of); of = NULL; /* picked up again later */ } else if (us.size != -1) { if (us.size == sb.st_size) /* nothing to do */ goto success; if (sb.st_size > us.size) { /* local file too long! */ warnx("%s: local file (%jd bytes) is longer " "than remote file (%jd bytes)", path, (intmax_t)sb.st_size, (intmax_t)us.size); goto failure; } /* we got it, open local file */ if ((of = fopen(path, "r+")) == NULL) { warn("%s: fopen()", path); goto failure; } /* check that it didn't move under our feet */ if (fstat(fileno(of), &nsb) == -1) { /* can't happen! */ warn("%s: fstat()", path); goto failure; } if (nsb.st_dev != sb.st_dev || nsb.st_ino != sb.st_ino || nsb.st_size != sb.st_size) { warnx("%s: file has changed", URL); fclose(of); of = NULL; sb = nsb; /* picked up again later */ } } /* seek to where we left off */ if (of != NULL && fseeko(of, url->offset, SEEK_SET) != 0) { warn("%s: fseeko()", path); fclose(of); of = NULL; /* picked up again later */ } } else if (m_flag && sb.st_size != -1) { /* mirror mode, local file exists */ if (sb.st_size == us.size && sb.st_mtime == us.mtime) goto success; } if (of == NULL) { /* * We don't yet have an output file; either this is a * vanilla run with no special flags, or the local and * remote files didn't match. */ if (url->offset > 0) { /* * We tried to restart a transfer, but for * some reason gave up - so we have to restart * from scratch if we want the whole file */ url->offset = 0; if ((f = fetchXGet(url, &us, flags)) == NULL) { warnx("%s: %s", URL, fetchLastErrString); goto failure; } if (sigint) goto signal; } /* construct a temp file name */ if (sb.st_size != -1 && S_ISREG(sb.st_mode)) { if ((slash = strrchr(path, '/')) == NULL) slash = path; else ++slash; if(tmppath != NULL) free(tmppath); asprintf(&tmppath, "%.*s.fetch.XXXXXX.%s", (int)(slash - path), path, slash); if (tmppath != NULL) { if (mkstemps(tmppath, strlen(slash) + 1) == -1) { warn("%s: mkstemps()", path); goto failure; } of = fopen(tmppath, "w"); chown(tmppath, sb.st_uid, sb.st_gid); chmod(tmppath, sb.st_mode & ALLPERMS); } } if (of == NULL) of = fopen(path, "w"); if (of == NULL) { warn("%s: open()", path); goto failure; } } count = url->offset; /* start the counter */ stat_start(&xs, path, us.size, count); sigalrm = siginfo = sigint = 0; /* suck in the data */ setvbuf(f, NULL, _IOFBF, B_size); signal(SIGINFO, sig_handler); while (!sigint) { if (us.size != -1 && us.size - count < B_size && us.size - count >= 0) size = us.size - count; else size = B_size; if (siginfo) { stat_end(&xs); siginfo = 0; } if (size == 0) break; if ((readcnt = fread(buf, 1, size, f)) < size) { if (ferror(f) && errno == EINTR && !sigint) clearerr(f); else if (readcnt == 0) break; } stat_update(&xs, count += readcnt); for (ptr = buf; readcnt > 0; ptr += wr, readcnt -= wr) if ((wr = fwrite(ptr, 1, readcnt, of)) < readcnt) { if (ferror(of) && errno == EINTR && !sigint) clearerr(of); else break; } if (readcnt != 0) break; } if (!sigalrm) sigalrm = ferror(f) && errno == ETIMEDOUT; signal(SIGINFO, SIG_DFL); stat_end(&xs); /* * If the transfer timed out or was interrupted, we still want to * set the mtime in case the file is not removed (-r or -R) and * the user later restarts the transfer. */ signal: /* set mtime of local file */ if (!n_flag && us.mtime && !o_stdout && of != NULL && (stat(path, &sb) != -1) && sb.st_mode & S_IFREG) { struct timeval tv[2]; fflush(of); tv[0].tv_sec = (long)(us.atime ? us.atime : us.mtime); tv[1].tv_sec = (long)us.mtime; tv[0].tv_usec = tv[1].tv_usec = 0; if (utimes(tmppath ? tmppath : path, tv)) warn("%s: utimes()", tmppath ? tmppath : path); } /* timed out or interrupted? */ if (sigalrm) warnx("transfer timed out"); if (sigint) { warnx("transfer interrupted"); goto failure; } /* timeout / interrupt before connection completley established? */ if (f == NULL) goto failure; if (!sigalrm) { /* check the status of our files */ if (ferror(f)) warn("%s", URL); if (ferror(of)) warn("%s", path); if (ferror(f) || ferror(of)) goto failure; } /* did the transfer complete normally? */ if (us.size != -1 && count < us.size) { warnx("%s appears to be truncated: %jd/%jd bytes", path, (intmax_t)count, (intmax_t)us.size); if(!o_stdout && a_flag && count > size_prev) { fclose(f); if (w_secs) sleep(w_secs); tries++; goto again; } goto failure_keep; } /* * If the transfer timed out and we didn't know how much to * expect, assume the worst (i.e. we didn't get all of it) */ if (sigalrm && us.size == -1) { warnx("%s may be truncated", path); goto failure_keep; } success: r = 0; if (tmppath != NULL && rename(tmppath, path) == -1) { warn("%s: rename()", path); goto failure_keep; } goto done; failure: if (of && of != stdout && !R_flag && !r_flag) if (stat(path, &sb) != -1 && (sb.st_mode & S_IFREG)) unlink(tmppath ? tmppath : path); if (R_flag && tmppath != NULL && sb.st_size == -1) rename(tmppath, path); /* ignore errors here */ failure_keep: r = -1; goto done; done: if (f) fclose(f); if (of && of != stdout) fclose(of); if (url) fetchFreeURL(url); if (tmppath != NULL) free(tmppath); return (r); } static void usage(void) { fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", "usage: fetch [-146AadFlMmnPpqRrsUv] [-B bytes] [--bind-address=host]", " [--ca-cert=file] [--ca-path=dir] [--cert=file] [--crl=file]", " [-i file] [--key=file] [-N file] [--no-passive] [--no-proxy=list]", " [--no-sslv3] [--no-tlsv1] [--no-verify-hostname] [--no-verify-peer]", " [-o file] [--referer=URL] [-S bytes] [-T seconds]", " [--user-agent=agent-string] [-w seconds] URL ...", " fetch [-146AadFlMmnPpqRrsUv] [-B bytes] [--bind-address=host]", " [--ca-cert=file] [--ca-path=dir] [--cert=file] [--crl=file]", " [-i file] [--key=file] [-N file] [--no-passive] [--no-proxy=list]", " [--no-sslv3] [--no-tlsv1] [--no-verify-hostname] [--no-verify-peer]", " [-o file] [--referer=URL] [-S bytes] [-T seconds]", " [--user-agent=agent-string] [-w seconds] -h host -f file [-c dir]"); } /* * Entry point */ int main(int argc, char *argv[]) { struct stat sb; struct sigaction sa; const char *p, *s; char *end, *q; int c, e, is_http, r; while ((c = getopt_long(argc, argv, "146AaB:bc:dFf:Hh:i:lMmN:nPpo:qRrS:sT:tUvw:", longopts, NULL)) != -1) switch (c) { case '1': once_flag = 1; break; case '4': family = PF_INET; break; case '6': family = PF_INET6; break; case 'A': A_flag = 1; break; case 'a': a_flag = 1; break; case 'B': B_size = (off_t)strtol(optarg, &end, 10); if (*optarg == '\0' || *end != '\0') errx(1, "invalid buffer size (%s)", optarg); break; case 'b': warnx("warning: the -b option is deprecated"); b_flag = 1; break; case 'c': c_dirname = optarg; break; case 'd': d_flag = 1; break; case 'F': F_flag = 1; break; case 'f': f_filename = optarg; break; case 'H': warnx("the -H option is now implicit, " "use -U to disable"); break; case 'h': h_hostname = optarg; break; case 'i': i_flag = 1; i_filename = optarg; break; case 'l': l_flag = 1; break; case 'o': o_flag = 1; o_filename = optarg; break; case 'M': case 'm': if (r_flag) errx(1, "the -m and -r flags " "are mutually exclusive"); m_flag = 1; break; case 'N': N_filename = optarg; break; case 'n': n_flag = 1; break; case 'P': case 'p': p_flag = 1; break; case 'q': v_level = 0; break; case 'R': R_flag = 1; break; case 'r': if (m_flag) errx(1, "the -m and -r flags " "are mutually exclusive"); r_flag = 1; break; case 'S': S_size = strtoll(optarg, &end, 10); if (*optarg == '\0' || *end != '\0') errx(1, "invalid size (%s)", optarg); break; case 's': s_flag = 1; break; case 'T': T_secs = strtol(optarg, &end, 10); if (*optarg == '\0' || *end != '\0') errx(1, "invalid timeout (%s)", optarg); break; case 't': t_flag = 1; warnx("warning: the -t option is deprecated"); break; case 'U': U_flag = 1; break; case 'v': v_level++; break; case 'w': a_flag = 1; w_secs = strtol(optarg, &end, 10); if (*optarg == '\0' || *end != '\0') errx(1, "invalid delay (%s)", optarg); break; case OPTION_BIND_ADDRESS: setenv("FETCH_BIND_ADDRESS", optarg, 1); break; case OPTION_NO_FTP_PASSIVE_MODE: setenv("FTP_PASSIVE_MODE", "no", 1); break; case OPTION_HTTP_REFERER: setenv("HTTP_REFERER", optarg, 1); break; case OPTION_HTTP_USER_AGENT: setenv("HTTP_USER_AGENT", optarg, 1); break; case OPTION_NO_PROXY: setenv("NO_PROXY", optarg, 1); break; case OPTION_SSL_CA_CERT_FILE: setenv("SSL_CA_CERT_FILE", optarg, 1); break; case OPTION_SSL_CA_CERT_PATH: setenv("SSL_CA_CERT_PATH", optarg, 1); break; case OPTION_SSL_CLIENT_CERT_FILE: setenv("SSL_CLIENT_CERT_FILE", optarg, 1); break; case OPTION_SSL_CLIENT_KEY_FILE: setenv("SSL_CLIENT_KEY_FILE", optarg, 1); break; case OPTION_SSL_CRL_FILE: setenv("SSL_CLIENT_CRL_FILE", optarg, 1); break; case OPTION_SSL_NO_SSL3: setenv("SSL_NO_SSL3", "", 1); break; case OPTION_SSL_NO_TLS1: setenv("SSL_NO_TLS1", "", 1); break; case OPTION_SSL_NO_VERIFY_HOSTNAME: setenv("SSL_NO_VERIFY_HOSTNAME", "", 1); break; case OPTION_SSL_NO_VERIFY_PEER: setenv("SSL_NO_VERIFY_PEER", "", 1); break; default: usage(); exit(1); } argc -= optind; argv += optind; if (h_hostname || f_filename || c_dirname) { if (!h_hostname || !f_filename || argc) { usage(); exit(1); } /* XXX this is a hack. */ if (strcspn(h_hostname, "@:/") != strlen(h_hostname)) errx(1, "invalid hostname"); if (asprintf(argv, "ftp://%s/%s/%s", h_hostname, c_dirname ? c_dirname : "", f_filename) == -1) errx(1, "%s", strerror(ENOMEM)); argc++; } if (!argc) { usage(); exit(1); } /* allocate buffer */ if (B_size < MINBUFSIZE) B_size = MINBUFSIZE; if ((buf = malloc(B_size)) == NULL) errx(1, "%s", strerror(ENOMEM)); /* timeouts */ if ((s = getenv("FTP_TIMEOUT")) != NULL) { ftp_timeout = strtol(s, &end, 10); if (*s == '\0' || *end != '\0' || ftp_timeout < 0) { warnx("FTP_TIMEOUT (%s) is not a positive integer", s); ftp_timeout = 0; } } if ((s = getenv("HTTP_TIMEOUT")) != NULL) { http_timeout = strtol(s, &end, 10); if (*s == '\0' || *end != '\0' || http_timeout < 0) { warnx("HTTP_TIMEOUT (%s) is not a positive integer", s); http_timeout = 0; } } /* signal handling */ sa.sa_flags = 0; sa.sa_handler = sig_handler; sigemptyset(&sa.sa_mask); sigaction(SIGALRM, &sa, NULL); sa.sa_flags = SA_RESETHAND; sigaction(SIGINT, &sa, NULL); fetchRestartCalls = 0; /* output file */ if (o_flag) { if (strcmp(o_filename, "-") == 0) { o_stdout = 1; } else if (stat(o_filename, &sb) == -1) { if (errno == ENOENT) { if (argc > 1) errx(1, "%s is not a directory", o_filename); } else { err(1, "%s", o_filename); } } else { if (sb.st_mode & S_IFDIR) o_directory = 1; } } /* check if output is to a tty (for progress report) */ v_tty = isatty(STDERR_FILENO); v_progress = v_tty && v_level > 0; if (v_progress) pgrp = getpgrp(); r = 0; /* authentication */ if (v_tty) fetchAuthMethod = query_auth; if (N_filename != NULL) if (setenv("NETRC", N_filename, 1) == -1) err(1, "setenv: cannot set NETRC=%s", N_filename); while (argc) { if ((p = strrchr(*argv, '/')) == NULL) p = *argv; else p++; if (!*p) p = "fetch.out"; fetchLastErrCode = 0; if (o_flag) { if (o_stdout) { e = fetch(*argv, "-", &is_http); } else if (o_directory) { asprintf(&q, "%s/%s", o_filename, p); e = fetch(*argv, q, &is_http); free(q); } else { e = fetch(*argv, o_filename, &is_http); } } else { e = fetch(*argv, p, &is_http); } if (sigint) kill(getpid(), SIGINT); if (e == 0 && once_flag) exit(0); if (e) { r = 1; if ((fetchLastErrCode && fetchLastErrCode != FETCH_AUTH && fetchLastErrCode != FETCH_UNAVAIL && fetchLastErrCode != FETCH_MOVED && fetchLastErrCode != FETCH_URL && fetchLastErrCode != FETCH_RESOLV && fetchLastErrCode != FETCH_UNKNOWN && (!is_http || ( fetchLastErrCode != FETCH_PROTO && fetchLastErrCode != FETCH_SERVER && fetchLastErrCode != FETCH_TEMP && fetchLastErrCode != FETCH_TIMEOUT )))) { if (w_secs && v_level) fprintf(stderr, "Waiting %ld seconds " "before retrying\n", w_secs); if (w_secs) sleep(w_secs); if (a_flag) continue; } } argc--, argv++; } exit(r); } diff --git a/usr.bin/find/find.c b/usr.bin/find/find.c index 6b9696ea71df..54b4fdd679e4 100644 --- a/usr.bin/find/find.c +++ b/usr.bin/find/find.c @@ -1,236 +1,234 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Cimarron D. Taylor of the University of California, Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - -#include #include #include #include #include #include #include #include #include #include #include "find.h" static int find_compare(const FTSENT * const *s1, const FTSENT * const *s2); /* * find_compare -- * tell fts_open() how to order the traversal of the hierarchy. * This variant gives lexicographical order, i.e., alphabetical * order within each directory. */ static int find_compare(const FTSENT * const *s1, const FTSENT * const *s2) { return (strcoll((*s1)->fts_name, (*s2)->fts_name)); } /* * find_formplan -- * process the command line and create a "plan" corresponding to the * command arguments. */ PLAN * find_formplan(char *argv[]) { PLAN *plan, *tail, *new; /* * for each argument in the command line, determine what kind of node * it is, create the appropriate node type and add the new plan node * to the end of the existing plan. The resulting plan is a linked * list of plan nodes. For example, the string: * * % find . -name foo -newer bar -print * * results in the plan: * * [-name foo]--> [-newer bar]--> [-print] * * in this diagram, `[-name foo]' represents the plan node generated * by c_name() with an argument of foo and `-->' represents the * plan->next pointer. */ for (plan = tail = NULL; *argv;) { if (!(new = find_create(&argv))) continue; if (plan == NULL) tail = plan = new; else { tail->next = new; tail = new; } } /* * if the user didn't specify one of -print, -ok or -exec, then -print * is assumed so we bracket the current expression with parens, if * necessary, and add a -print node on the end. */ if (!isoutput) { OPTION *p; char **argv1 = 0; if (plan == NULL) { p = lookup_option("-print"); new = (p->create)(p, &argv1); tail = plan = new; } else { p = lookup_option("("); new = (p->create)(p, &argv1); new->next = plan; plan = new; p = lookup_option(")"); new = (p->create)(p, &argv1); tail->next = new; tail = new; p = lookup_option("-print"); new = (p->create)(p, &argv1); tail->next = new; tail = new; } } /* * the command line has been completely processed into a search plan * except for the (, ), !, and -o operators. Rearrange the plan so * that the portions of the plan which are affected by the operators * are moved into operator nodes themselves. For example: * * [!]--> [-name foo]--> [-print] * * becomes * * [! [-name foo] ]--> [-print] * * and * * [(]--> [-depth]--> [-name foo]--> [)]--> [-print] * * becomes * * [expr [-depth]-->[-name foo] ]--> [-print] * * operators are handled in order of precedence. */ plan = paren_squish(plan); /* ()'s */ plan = not_squish(plan); /* !'s */ plan = or_squish(plan); /* -o's */ return (plan); } FTS *tree; /* pointer to top of FTS hierarchy */ /* * find_execute -- * take a search plan and an array of search paths and executes the plan * over all FTSENT's returned for the given search paths. */ int find_execute(PLAN *plan, char *paths[]) { FTSENT *entry; PLAN *p; int e; tree = fts_open(paths, ftsoptions, (issort ? find_compare : NULL)); if (tree == NULL) err(1, "ftsopen"); exitstatus = 0; while (errno = 0, (entry = fts_read(tree)) != NULL) { if (maxdepth != -1 && entry->fts_level >= maxdepth) { if (fts_set(tree, entry, FTS_SKIP)) err(1, "%s", entry->fts_path); } switch (entry->fts_info) { case FTS_D: if (isdepth) continue; break; case FTS_DP: if (!isdepth) continue; break; case FTS_DNR: case FTS_NS: if (ignore_readdir_race && entry->fts_errno == ENOENT && entry->fts_level > 0) continue; /* FALLTHROUGH */ case FTS_ERR: (void)fflush(stdout); warnx("%s: %s", entry->fts_path, strerror(entry->fts_errno)); exitstatus = 1; continue; #if defined(FTS_W) && defined(FTS_WHITEOUT) case FTS_W: if (ftsoptions & FTS_WHITEOUT) break; continue; #endif /* FTS_W */ } #define BADCH " \t\n\\'\"" if (isxargs && strpbrk(entry->fts_path, BADCH)) { (void)fflush(stdout); warnx("%s: illegal path", entry->fts_path); exitstatus = 1; continue; } if (mindepth != -1 && entry->fts_level < mindepth) continue; /* * Call all the functions in the execution plan until one is * false or all have been executed. This is where we do all * the work specified by the user on the command line. */ for (p = plan; p && (p->execute)(p, entry); p = p->next); } e = errno; finish_execplus(); if (e && (!ignore_readdir_race || e != ENOENT)) errc(1, e, "fts_read"); return (exitstatus); } diff --git a/usr.bin/find/function.c b/usr.bin/find/function.c index 03e49fc62b64..43aa2301439c 100644 --- a/usr.bin/find/function.c +++ b/usr.bin/find/function.c @@ -1,1812 +1,1810 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Cimarron D. Taylor of the University of California, Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "find.h" static PLAN *palloc(OPTION *); static long long find_parsenum(PLAN *, const char *, char *, char *); static long long find_parsetime(PLAN *, const char *, char *); static char *nextarg(OPTION *, char ***); extern char **environ; static PLAN *lastexecplus = NULL; #define COMPARE(a, b) do { \ switch (plan->flags & F_ELG_MASK) { \ case F_EQUAL: \ return (a == b); \ case F_LESSTHAN: \ return (a < b); \ case F_GREATER: \ return (a > b); \ default: \ abort(); \ } \ } while(0) static PLAN * palloc(OPTION *option) { PLAN *new; if ((new = malloc(sizeof(PLAN))) == NULL) err(1, NULL); new->execute = option->execute; new->flags = option->flags; new->next = NULL; return new; } /* * find_parsenum -- * Parse a string of the form [+-]# and return the value. */ static long long find_parsenum(PLAN *plan, const char *option, char *vp, char *endch) { long long value; char *endchar, *str; /* Pointer to character ending conversion. */ /* Determine comparison from leading + or -. */ str = vp; switch (*str) { case '+': ++str; plan->flags |= F_GREATER; break; case '-': ++str; plan->flags |= F_LESSTHAN; break; default: plan->flags |= F_EQUAL; break; } /* * Convert the string with strtoq(). Note, if strtoq() returns zero * and endchar points to the beginning of the string we know we have * a syntax error. */ value = strtoq(str, &endchar, 10); if (value == 0 && endchar == str) errx(1, "%s: %s: illegal numeric value", option, vp); if (endchar[0] && endch == NULL) errx(1, "%s: %s: illegal trailing character", option, vp); if (endch) *endch = endchar[0]; return value; } /* * find_parsetime -- * Parse a string of the form [+-]([0-9]+[smhdw]?)+ and return the value. */ static long long find_parsetime(PLAN *plan, const char *option, char *vp) { long long secs, value; char *str, *unit; /* Pointer to character ending conversion. */ /* Determine comparison from leading + or -. */ str = vp; switch (*str) { case '+': ++str; plan->flags |= F_GREATER; break; case '-': ++str; plan->flags |= F_LESSTHAN; break; default: plan->flags |= F_EQUAL; break; } value = strtoq(str, &unit, 10); if (value == 0 && unit == str) { errx(1, "%s: %s: illegal time value", option, vp); /* NOTREACHED */ } if (*unit == '\0') return value; /* Units syntax. */ secs = 0; for (;;) { switch(*unit) { case 's': /* seconds */ secs += value; break; case 'm': /* minutes */ secs += value * 60; break; case 'h': /* hours */ secs += value * 3600; break; case 'd': /* days */ secs += value * 86400; break; case 'w': /* weeks */ secs += value * 604800; break; default: errx(1, "%s: %s: bad unit '%c'", option, vp, *unit); /* NOTREACHED */ } str = unit + 1; if (*str == '\0') /* EOS */ break; value = strtoq(str, &unit, 10); if (value == 0 && unit == str) { errx(1, "%s: %s: illegal time value", option, vp); /* NOTREACHED */ } if (*unit == '\0') { errx(1, "%s: %s: missing trailing unit", option, vp); /* NOTREACHED */ } } plan->flags |= F_EXACTTIME; return secs; } /* * nextarg -- * Check that another argument still exists, return a pointer to it, * and increment the argument vector pointer. */ static char * nextarg(OPTION *option, char ***argvp) { char *arg; if ((arg = **argvp) == NULL) errx(1, "%s: requires additional arguments", option->name); (*argvp)++; return arg; } /* nextarg() */ /* * The value of n for the inode times (atime, birthtime, ctime, mtime) is a * range, i.e. n matches from (n - 1) to n 24 hour periods. This interacts * with -n, such that "-mtime -1" would be less than 0 days, which isn't what * the user wanted. Correct so that -1 is "less than 1". */ #define TIME_CORRECT(p) \ if (((p)->flags & F_ELG_MASK) == F_LESSTHAN) \ ++((p)->t_data.tv_sec); /* * -[acm]min n functions -- * * True if the difference between the * file access time (-amin) * file birth time (-Bmin) * last change of file status information (-cmin) * file modification time (-mmin) * and the current time is n min periods. */ int f_Xmin(PLAN *plan, FTSENT *entry) { if (plan->flags & F_TIME_C) { COMPARE((now - entry->fts_statp->st_ctime + 60 - 1) / 60, plan->t_data.tv_sec); } else if (plan->flags & F_TIME_A) { COMPARE((now - entry->fts_statp->st_atime + 60 - 1) / 60, plan->t_data.tv_sec); #if HAVE_STRUCT_STAT_ST_BIRTHTIME } else if (plan->flags & F_TIME_B) { COMPARE((now - entry->fts_statp->st_birthtime + 60 - 1) / 60, plan->t_data.tv_sec); #endif } else { COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) / 60, plan->t_data.tv_sec); } } PLAN * c_Xmin(OPTION *option, char ***argvp) { char *nmins; PLAN *new; nmins = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; new = palloc(option); new->t_data.tv_sec = find_parsenum(new, option->name, nmins, NULL); new->t_data.tv_nsec = 0; TIME_CORRECT(new); return new; } /* * -[acm]time n functions -- * * True if the difference between the * file access time (-atime) * file birth time (-Btime) * last change of file status information (-ctime) * file modification time (-mtime) * and the current time is n 24 hour periods. */ int f_Xtime(PLAN *plan, FTSENT *entry) { time_t xtime; if (plan->flags & F_TIME_A) xtime = entry->fts_statp->st_atime; #if HAVE_STRUCT_STAT_ST_BIRTHTIME else if (plan->flags & F_TIME_B) xtime = entry->fts_statp->st_birthtime; #endif else if (plan->flags & F_TIME_C) xtime = entry->fts_statp->st_ctime; else xtime = entry->fts_statp->st_mtime; if (plan->flags & F_EXACTTIME) COMPARE(now - xtime, plan->t_data.tv_sec); else COMPARE((now - xtime + 86400 - 1) / 86400, plan->t_data.tv_sec); } PLAN * c_Xtime(OPTION *option, char ***argvp) { char *value; PLAN *new; value = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; new = palloc(option); new->t_data.tv_sec = find_parsetime(new, option->name, value); new->t_data.tv_nsec = 0; if (!(new->flags & F_EXACTTIME)) TIME_CORRECT(new); return new; } /* * -maxdepth/-mindepth n functions -- * * Does the same as -prune if the level of the current file is * greater/less than the specified maximum/minimum depth. * * Note that -maxdepth and -mindepth are handled specially in * find_execute() so their f_* functions are set to f_always_true(). */ PLAN * c_mXXdepth(OPTION *option, char ***argvp) { char *dstr; PLAN *new; dstr = nextarg(option, argvp); if (dstr[0] == '-') /* all other errors handled by find_parsenum() */ errx(1, "%s: %s: value must be positive", option->name, dstr); new = palloc(option); if (option->flags & F_MAXDEPTH) maxdepth = find_parsenum(new, option->name, dstr, NULL); else mindepth = find_parsenum(new, option->name, dstr, NULL); return new; } #ifdef ACL_TYPE_NFS4 /* * -acl function -- * * Show files with EXTENDED ACL attributes. */ int f_acl(PLAN *plan __unused, FTSENT *entry) { acl_t facl; acl_type_t acl_type; int acl_supported = 0, ret, trivial; if (S_ISLNK(entry->fts_statp->st_mode)) return 0; ret = pathconf(entry->fts_accpath, _PC_ACL_NFS4); if (ret > 0) { acl_supported = 1; acl_type = ACL_TYPE_NFS4; } else if (ret < 0 && errno != EINVAL) { warn("%s", entry->fts_accpath); return (0); } if (acl_supported == 0) { ret = pathconf(entry->fts_accpath, _PC_ACL_EXTENDED); if (ret > 0) { acl_supported = 1; acl_type = ACL_TYPE_ACCESS; } else if (ret < 0 && errno != EINVAL) { warn("%s", entry->fts_accpath); return (0); } } if (acl_supported == 0) return (0); facl = acl_get_file(entry->fts_accpath, acl_type); if (facl == NULL) { warn("%s", entry->fts_accpath); return (0); } ret = acl_is_trivial_np(facl, &trivial); acl_free(facl); if (ret) { warn("%s", entry->fts_accpath); return (0); } if (trivial) return (0); return (1); } #endif PLAN * c_acl(OPTION *option, char ***argvp __unused) { ftsoptions &= ~FTS_NOSTAT; return (palloc(option)); } /* * -delete functions -- * * True always. Makes its best shot and continues on regardless. */ int f_delete(PLAN *plan __unused, FTSENT *entry) { /* ignore these from fts */ if (strcmp(entry->fts_accpath, ".") == 0 || strcmp(entry->fts_accpath, "..") == 0) return 1; /* sanity check */ if (isdepth == 0 || /* depth off */ (ftsoptions & FTS_NOSTAT)) /* not stat()ing */ errx(1, "-delete: insecure options got turned on"); if (!(ftsoptions & FTS_PHYSICAL) || /* physical off */ (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ errx(1, "-delete: forbidden when symlinks are followed"); /* Potentially unsafe - do not accept relative paths whatsoever */ if (entry->fts_level > FTS_ROOTLEVEL && strchr(entry->fts_accpath, '/') != NULL) errx(1, "-delete: %s: relative path potentially not safe", entry->fts_accpath); #if HAVE_STRUCT_STAT_ST_FLAGS /* Turn off user immutable bits if running as root */ if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && geteuid() == 0) lchflags(entry->fts_accpath, entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); #endif /* rmdir directories, unlink everything else */ if (S_ISDIR(entry->fts_statp->st_mode)) { if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) warn("-delete: rmdir(%s)", entry->fts_path); } else { if (unlink(entry->fts_accpath) < 0) warn("-delete: unlink(%s)", entry->fts_path); } /* "succeed" */ return 1; } PLAN * c_delete(OPTION *option, char ***argvp __unused) { ftsoptions &= ~FTS_NOSTAT; /* no optimise */ isoutput = 1; /* possible output */ isdepth = 1; /* -depth implied */ /* * Try to avoid the confusing error message about relative paths * being potentially not safe. */ if (ftsoptions & FTS_NOCHDIR) errx(1, "%s: forbidden when the current directory cannot be opened", "-delete"); return palloc(option); } /* * always_true -- * * Always true, used for -maxdepth, -mindepth, -xdev, -follow, and -true */ int f_always_true(PLAN *plan __unused, FTSENT *entry __unused) { return 1; } /* * -depth functions -- * * With argument: True if the file is at level n. * Without argument: Always true, causes descent of the directory hierarchy * to be done so that all entries in a directory are acted on before the * directory itself. */ int f_depth(PLAN *plan, FTSENT *entry) { if (plan->flags & F_DEPTH) COMPARE(entry->fts_level, plan->d_data); else return 1; } PLAN * c_depth(OPTION *option, char ***argvp) { PLAN *new; char *str; new = palloc(option); str = **argvp; if (str && !(new->flags & F_DEPTH)) { /* skip leading + or - */ if (*str == '+' || *str == '-') str++; /* skip sign */ if (*str == '+' || *str == '-') str++; if (isdigit(*str)) new->flags |= F_DEPTH; } if (new->flags & F_DEPTH) { /* -depth n */ char *ndepth; ndepth = nextarg(option, argvp); new->d_data = find_parsenum(new, option->name, ndepth, NULL); } else { /* -d */ isdepth = 1; } return new; } /* * -empty functions -- * * True if the file or directory is empty */ int f_empty(PLAN *plan __unused, FTSENT *entry) { if (S_ISREG(entry->fts_statp->st_mode) && entry->fts_statp->st_size == 0) return 1; if (S_ISDIR(entry->fts_statp->st_mode)) { struct dirent *dp; int empty; DIR *dir; empty = 1; dir = opendir(entry->fts_accpath); if (dir == NULL) return 0; for (dp = readdir(dir); dp; dp = readdir(dir)) if (dp->d_name[0] != '.' || (dp->d_name[1] != '\0' && (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) { empty = 0; break; } closedir(dir); return empty; } return 0; } PLAN * c_empty(OPTION *option, char ***argvp __unused) { ftsoptions &= ~FTS_NOSTAT; return palloc(option); } /* * [-exec | -execdir | -ok] utility [arg ... ] ; functions -- * * True if the executed utility returns a zero value as exit status. * The end of the primary expression is delimited by a semicolon. If * "{}" occurs anywhere, it gets replaced by the current pathname, * or, in the case of -execdir, the current basename (filename * without leading directory prefix). For -exec and -ok, * the current directory for the execution of utility is the same as * the current directory when the find utility was started, whereas * for -execdir, it is the directory the file resides in. * * The primary -ok differs from -exec in that it requests affirmation * of the user before executing the utility. */ int f_exec(PLAN *plan, FTSENT *entry) { int cnt; pid_t pid; int status; char *file; if (entry == NULL && plan->flags & F_EXECPLUS) { if (plan->e_ppos == plan->e_pbnum) return (1); plan->e_argv[plan->e_ppos] = NULL; goto doexec; } /* XXX - if file/dir ends in '/' this will not work -- can it? */ if ((plan->flags & F_EXECDIR) && \ (file = strrchr(entry->fts_path, '/'))) file++; else file = entry->fts_path; if (plan->flags & F_EXECPLUS) { if ((plan->e_argv[plan->e_ppos] = strdup(file)) == NULL) err(1, NULL); plan->e_len[plan->e_ppos] = strlen(file); plan->e_psize += plan->e_len[plan->e_ppos]; if (++plan->e_ppos < plan->e_pnummax && plan->e_psize < plan->e_psizemax) return (1); plan->e_argv[plan->e_ppos] = NULL; } else { for (cnt = 0; plan->e_argv[cnt]; ++cnt) if (plan->e_len[cnt]) brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], file, plan->e_len[cnt]); } doexec: if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv)) return 0; /* make sure find output is interspersed correctly with subprocesses */ fflush(stdout); fflush(stderr); switch (pid = fork()) { case -1: err(1, "fork"); /* NOTREACHED */ case 0: /* change dir back from where we started */ if (!(plan->flags & F_EXECDIR) && !(ftsoptions & FTS_NOCHDIR) && fchdir(dotfd)) { warn("chdir"); _exit(1); } execvp(plan->e_argv[0], plan->e_argv); warn("%s", plan->e_argv[0]); _exit(1); } if (plan->flags & F_EXECPLUS) { while (--plan->e_ppos >= plan->e_pbnum) free(plan->e_argv[plan->e_ppos]); plan->e_ppos = plan->e_pbnum; plan->e_psize = plan->e_pbsize; } pid = waitpid(pid, &status, 0); if (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)) return (1); if (plan->flags & F_EXECPLUS) { exitstatus = 1; return (1); } return (0); } /* * c_exec, c_execdir, c_ok -- * build three parallel arrays, one with pointers to the strings passed * on the command line, one with (possibly duplicated) pointers to the * argv array, and one with integer values that are lengths of the * strings, but also flags meaning that the string has to be massaged. */ PLAN * c_exec(OPTION *option, char ***argvp) { PLAN *new; /* node returned */ long argmax; int cnt, i; char **argv, **ap, **ep, *p; /* This would defeat -execdir's intended security. */ if (option->flags & F_EXECDIR && ftsoptions & FTS_NOCHDIR) errx(1, "%s: forbidden when the current directory cannot be opened", "-execdir"); /* XXX - was in c_execdir, but seems unnecessary!? ftsoptions &= ~FTS_NOSTAT; */ isoutput = 1; /* XXX - this is a change from the previous coding */ new = palloc(option); for (ap = argv = *argvp;; ++ap) { if (!*ap) errx(1, "%s: no terminating \";\" or \"+\"", option->name); if (**ap == ';') break; if (**ap == '+' && ap != argv && strcmp(*(ap - 1), "{}") == 0) { new->flags |= F_EXECPLUS; break; } } if (ap == argv) errx(1, "%s: no command specified", option->name); cnt = ap - *argvp + 1; if (new->flags & F_EXECPLUS) { new->e_ppos = new->e_pbnum = cnt - 2; if ((argmax = sysconf(_SC_ARG_MAX)) == -1) { warn("sysconf(_SC_ARG_MAX)"); argmax = _POSIX_ARG_MAX; } argmax -= 1024; for (ep = environ; *ep != NULL; ep++) argmax -= strlen(*ep) + 1 + sizeof(*ep); argmax -= 1 + sizeof(*ep); /* * Ensure that -execdir ... {} + does not mix files * from different directories in one invocation. * Files from the same directory should be handled * in one invocation but there is no code for it. */ new->e_pnummax = new->flags & F_EXECDIR ? 1 : argmax / 16; argmax -= sizeof(char *) * new->e_pnummax; if (argmax <= 0) errx(1, "no space for arguments"); new->e_psizemax = argmax; new->e_pbsize = 0; cnt += new->e_pnummax + 1; new->e_next = lastexecplus; lastexecplus = new; } if ((new->e_argv = malloc(cnt * sizeof(char *))) == NULL) err(1, NULL); if ((new->e_orig = malloc(cnt * sizeof(char *))) == NULL) err(1, NULL); if ((new->e_len = malloc(cnt * sizeof(int))) == NULL) err(1, NULL); for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { new->e_orig[cnt] = *argv; if (new->flags & F_EXECPLUS) new->e_pbsize += strlen(*argv) + 1; for (p = *argv; *p; ++p) if (!(new->flags & F_EXECPLUS) && p[0] == '{' && p[1] == '}') { if ((new->e_argv[cnt] = malloc(MAXPATHLEN)) == NULL) err(1, NULL); new->e_len[cnt] = MAXPATHLEN; break; } if (!*p) { new->e_argv[cnt] = *argv; new->e_len[cnt] = 0; } } if (new->flags & F_EXECPLUS) { new->e_psize = new->e_pbsize; cnt--; for (i = 0; i < new->e_pnummax; i++) { new->e_argv[cnt] = NULL; new->e_len[cnt] = 0; cnt++; } argv = ap; goto done; } new->e_argv[cnt] = new->e_orig[cnt] = NULL; done: *argvp = argv + 1; return new; } /* Finish any pending -exec ... {} + functions. */ void finish_execplus(void) { PLAN *p; p = lastexecplus; while (p != NULL) { (p->execute)(p, NULL); p = p->e_next; } } #if HAVE_STRUCT_STAT_ST_FLAGS int f_flags(PLAN *plan, FTSENT *entry) { u_long flags; flags = entry->fts_statp->st_flags; if (plan->flags & F_ATLEAST) return (flags | plan->fl_flags) == flags && !(flags & plan->fl_notflags); else if (plan->flags & F_ANY) return (flags & plan->fl_flags) || (flags | plan->fl_notflags) != flags; else return flags == plan->fl_flags && !(plan->fl_flags & plan->fl_notflags); } PLAN * c_flags(OPTION *option, char ***argvp) { char *flags_str; PLAN *new; u_long flags, notflags; flags_str = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; new = palloc(option); if (*flags_str == '-') { new->flags |= F_ATLEAST; flags_str++; } else if (*flags_str == '+') { new->flags |= F_ANY; flags_str++; } if (strtofflags(&flags_str, &flags, ¬flags) == 1) errx(1, "%s: %s: illegal flags string", option->name, flags_str); new->fl_flags = flags; new->fl_notflags = notflags; return new; } #endif /* * -follow functions -- * * Always true, causes symbolic links to be followed on a global * basis. */ PLAN * c_follow(OPTION *option, char ***argvp __unused) { ftsoptions &= ~FTS_PHYSICAL; ftsoptions |= FTS_LOGICAL; return palloc(option); } #if HAVE_STRUCT_STATFS_F_FSTYPENAME /* * -fstype functions -- * * True if the file is of a certain type. */ int f_fstype(PLAN *plan, FTSENT *entry) { static dev_t curdev; /* need a guaranteed illegal dev value */ static int first = 1; struct statfs sb; static int val_flags; static char fstype[sizeof(sb.f_fstypename)]; char *p, save[2] = {0,0}; if ((plan->flags & F_MTMASK) == F_MTUNKNOWN) return 0; /* Only check when we cross mount point. */ if (first || curdev != entry->fts_statp->st_dev) { curdev = entry->fts_statp->st_dev; /* * Statfs follows symlinks; find wants the link's filesystem, * not where it points. */ if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE) { if ((p = strrchr(entry->fts_accpath, '/')) != NULL) ++p; else p = entry->fts_accpath; save[0] = p[0]; p[0] = '.'; save[1] = p[1]; p[1] = '\0'; } else p = NULL; if (statfs(entry->fts_accpath, &sb)) { if (!ignore_readdir_race || errno != ENOENT) { warn("statfs: %s", entry->fts_accpath); exitstatus = 1; } return 0; } if (p) { p[0] = save[0]; p[1] = save[1]; } first = 0; /* * Further tests may need both of these values, so * always copy both of them. */ val_flags = sb.f_flags; strlcpy(fstype, sb.f_fstypename, sizeof(fstype)); } switch (plan->flags & F_MTMASK) { case F_MTFLAG: return val_flags & plan->mt_data; case F_MTTYPE: return (strncmp(fstype, plan->c_data, sizeof(fstype)) == 0); default: abort(); } } PLAN * c_fstype(OPTION *option, char ***argvp) { char *fsname; PLAN *new; fsname = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; new = palloc(option); switch (*fsname) { case 'l': if (!strcmp(fsname, "local")) { new->flags |= F_MTFLAG; new->mt_data = MNT_LOCAL; return new; } break; case 'r': if (!strcmp(fsname, "rdonly")) { new->flags |= F_MTFLAG; new->mt_data = MNT_RDONLY; return new; } break; } new->flags |= F_MTTYPE; new->c_data = fsname; return new; } #endif /* * -group gname functions -- * * True if the file belongs to the group gname. If gname is numeric and * an equivalent of the getgrnam() function does not return a valid group * name, gname is taken as a group ID. */ int f_group(PLAN *plan, FTSENT *entry) { COMPARE(entry->fts_statp->st_gid, plan->g_data); } PLAN * c_group(OPTION *option, char ***argvp) { char *gname; PLAN *new; struct group *g; gid_t gid; gname = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; new = palloc(option); g = getgrnam(gname); if (g == NULL) { char* cp = gname; if (gname[0] == '-' || gname[0] == '+') gname++; gid = atoi(gname); if (gid == 0 && gname[0] != '0') errx(1, "%s: %s: no such group", option->name, gname); gid = find_parsenum(new, option->name, cp, NULL); } else gid = g->gr_gid; new->g_data = gid; return new; } /* * -ignore_readdir_race functions -- * * Always true. Ignore errors which occur if a file or a directory * in a starting point gets deleted between reading the name and calling * stat on it while find is traversing the starting point. */ PLAN * c_ignore_readdir_race(OPTION *option, char ***argvp __unused) { if (strcmp(option->name, "-ignore_readdir_race") == 0) ignore_readdir_race = 1; else ignore_readdir_race = 0; return palloc(option); } /* * -inum n functions -- * * True if the file has inode # n. */ int f_inum(PLAN *plan, FTSENT *entry) { COMPARE(entry->fts_statp->st_ino, plan->i_data); } PLAN * c_inum(OPTION *option, char ***argvp) { char *inum_str; PLAN *new; inum_str = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; new = palloc(option); new->i_data = find_parsenum(new, option->name, inum_str, NULL); return new; } /* * -samefile FN * * True if the file has the same inode (eg hard link) FN */ /* f_samefile is just f_inum */ PLAN * c_samefile(OPTION *option, char ***argvp) { char *fn; PLAN *new; struct stat sb; int error; fn = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; new = palloc(option); if (ftsoptions & FTS_PHYSICAL) error = lstat(fn, &sb); else error = stat(fn, &sb); if (error != 0) err(1, "%s", fn); new->i_data = sb.st_ino; return new; } /* * -links n functions -- * * True if the file has n links. */ int f_links(PLAN *plan, FTSENT *entry) { COMPARE(entry->fts_statp->st_nlink, plan->l_data); } PLAN * c_links(OPTION *option, char ***argvp) { char *nlinks; PLAN *new; nlinks = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; new = palloc(option); new->l_data = (nlink_t)find_parsenum(new, option->name, nlinks, NULL); return new; } /* * -ls functions -- * * Always true - prints the current entry to stdout in "ls" format. */ int f_ls(PLAN *plan __unused, FTSENT *entry) { printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); return 1; } PLAN * c_ls(OPTION *option, char ***argvp __unused) { ftsoptions &= ~FTS_NOSTAT; isoutput = 1; return palloc(option); } /* * -name functions -- * * True if the basename of the filename being examined * matches pattern using Pattern Matching Notation S3.14 */ int f_name(PLAN *plan, FTSENT *entry) { char fn[PATH_MAX]; const char *name; ssize_t len; if (plan->flags & F_LINK) { /* * The below test both avoids obviously useless readlink() * calls and ensures that symlinks with existent target do * not match if symlinks are being followed. * Assumption: fts will stat all symlinks that are to be * followed and will return the stat information. */ if (entry->fts_info != FTS_NSOK && entry->fts_info != FTS_SL && entry->fts_info != FTS_SLNONE) return 0; len = readlink(entry->fts_accpath, fn, sizeof(fn) - 1); if (len == -1) return 0; fn[len] = '\0'; name = fn; } else name = entry->fts_name; return !fnmatch(plan->c_data, name, plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); } PLAN * c_name(OPTION *option, char ***argvp) { char *pattern; PLAN *new; pattern = nextarg(option, argvp); new = palloc(option); new->c_data = pattern; return new; } /* * -newer file functions -- * * True if the current file has been modified more recently * then the modification time of the file named by the pathname * file. */ int f_newer(PLAN *plan, FTSENT *entry) { struct timespec ft; if (plan->flags & F_TIME_C) ft = entry->fts_statp->st_ctim; #if HAVE_STRUCT_STAT_ST_BIRTHTIME else if (plan->flags & F_TIME_A) ft = entry->fts_statp->st_atim; else if (plan->flags & F_TIME_B) ft = entry->fts_statp->st_birthtim; #endif else ft = entry->fts_statp->st_mtim; return (ft.tv_sec > plan->t_data.tv_sec || (ft.tv_sec == plan->t_data.tv_sec && ft.tv_nsec > plan->t_data.tv_nsec)); } PLAN * c_newer(OPTION *option, char ***argvp) { char *fn_or_tspec; PLAN *new; struct stat sb; int error; fn_or_tspec = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; new = palloc(option); /* compare against what */ if (option->flags & F_TIME2_T) { new->t_data.tv_sec = get_date(fn_or_tspec); if (new->t_data.tv_sec == (time_t) -1) errx(1, "Can't parse date/time: %s", fn_or_tspec); /* Use the seconds only in the comparison. */ new->t_data.tv_nsec = 999999999; } else { if (ftsoptions & FTS_PHYSICAL) error = lstat(fn_or_tspec, &sb); else error = stat(fn_or_tspec, &sb); if (error != 0) err(1, "%s", fn_or_tspec); if (option->flags & F_TIME2_C) new->t_data = sb.st_ctim; else if (option->flags & F_TIME2_A) new->t_data = sb.st_atim; #if HAVE_STRUCT_STAT_ST_BIRTHTIME else if (option->flags & F_TIME2_B) new->t_data = sb.st_birthtim; #endif else new->t_data = sb.st_mtim; } return new; } /* * -nogroup functions -- * * True if file belongs to a user ID for which the equivalent * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. */ int f_nogroup(PLAN *plan __unused, FTSENT *entry) { return group_from_gid(entry->fts_statp->st_gid, 1) == NULL; } PLAN * c_nogroup(OPTION *option, char ***argvp __unused) { ftsoptions &= ~FTS_NOSTAT; return palloc(option); } /* * -nouser functions -- * * True if file belongs to a user ID for which the equivalent * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. */ int f_nouser(PLAN *plan __unused, FTSENT *entry) { return user_from_uid(entry->fts_statp->st_uid, 1) == NULL; } PLAN * c_nouser(OPTION *option, char ***argvp __unused) { ftsoptions &= ~FTS_NOSTAT; return palloc(option); } /* * -path functions -- * * True if the path of the filename being examined * matches pattern using Pattern Matching Notation S3.14 */ int f_path(PLAN *plan, FTSENT *entry) { return !fnmatch(plan->c_data, entry->fts_path, plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); } /* c_path is the same as c_name */ /* * -perm functions -- * * The mode argument is used to represent file mode bits. If it starts * with a leading digit, it's treated as an octal mode, otherwise as a * symbolic mode. */ int f_perm(PLAN *plan, FTSENT *entry) { mode_t mode; mode = entry->fts_statp->st_mode & (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); if (plan->flags & F_ATLEAST) return (plan->m_data | mode) == mode; else if (plan->flags & F_ANY) return (mode & plan->m_data); else return mode == plan->m_data; /* NOTREACHED */ } PLAN * c_perm(OPTION *option, char ***argvp) { char *perm; PLAN *new; mode_t *set; perm = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; new = palloc(option); if (*perm == '-') { new->flags |= F_ATLEAST; ++perm; } else if (*perm == '+') { new->flags |= F_ANY; ++perm; } if ((set = setmode(perm)) == NULL) errx(1, "%s: %s: illegal mode string", option->name, perm); new->m_data = getmode(set, 0); free(set); return new; } /* * -print functions -- * * Always true, causes the current pathname to be written to * standard output. */ int f_print(PLAN *plan __unused, FTSENT *entry) { (void)puts(entry->fts_path); return 1; } PLAN * c_print(OPTION *option, char ***argvp __unused) { isoutput = 1; return palloc(option); } /* * -print0 functions -- * * Always true, causes the current pathname to be written to * standard output followed by a NUL character */ int f_print0(PLAN *plan __unused, FTSENT *entry) { fputs(entry->fts_path, stdout); fputc('\0', stdout); return 1; } /* c_print0 is the same as c_print */ /* * -prune functions -- * * Prune a portion of the hierarchy. */ int f_prune(PLAN *plan __unused, FTSENT *entry) { if (fts_set(tree, entry, FTS_SKIP)) err(1, "%s", entry->fts_path); return 1; } /* c_prune == c_simple */ /* * -regex functions -- * * True if the whole path of the file matches pattern using * regular expression. */ int f_regex(PLAN *plan, FTSENT *entry) { char *str; int len; regex_t *pre; regmatch_t pmatch; int errcode; char errbuf[LINE_MAX]; int matched; pre = plan->re_data; str = entry->fts_path; len = strlen(str); matched = 0; pmatch.rm_so = 0; pmatch.rm_eo = len; errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND); if (errcode != 0 && errcode != REG_NOMATCH) { regerror(errcode, pre, errbuf, sizeof errbuf); errx(1, "%s: %s", plan->flags & F_IGNCASE ? "-iregex" : "-regex", errbuf); } if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len) matched = 1; return matched; } PLAN * c_regex(OPTION *option, char ***argvp) { PLAN *new; char *pattern; regex_t *pre; int errcode; char errbuf[LINE_MAX]; if ((pre = malloc(sizeof(regex_t))) == NULL) err(1, NULL); pattern = nextarg(option, argvp); if ((errcode = regcomp(pre, pattern, regexp_flags | (option->flags & F_IGNCASE ? REG_ICASE : 0))) != 0) { regerror(errcode, pre, errbuf, sizeof errbuf); errx(1, "%s: %s: %s", option->flags & F_IGNCASE ? "-iregex" : "-regex", pattern, errbuf); } new = palloc(option); new->re_data = pre; return new; } /* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or, c_true, c_false */ PLAN * c_simple(OPTION *option, char ***argvp __unused) { return palloc(option); } /* * -size n[c] functions -- * * True if the file size in bytes, divided by an implementation defined * value and rounded up to the next integer, is n. If n is followed by * one of c k M G T P, the size is in bytes, kilobytes, * megabytes, gigabytes, terabytes or petabytes respectively. */ #define FIND_SIZE 512 static int divsize = 1; int f_size(PLAN *plan, FTSENT *entry) { off_t size; size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / FIND_SIZE : entry->fts_statp->st_size; COMPARE(size, plan->o_data); } PLAN * c_size(OPTION *option, char ***argvp) { char *size_str; PLAN *new; char endch; off_t scale; size_str = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; new = palloc(option); endch = 'c'; new->o_data = find_parsenum(new, option->name, size_str, &endch); if (endch != '\0') { divsize = 0; switch (endch) { case 'c': /* characters */ scale = 0x1LL; break; case 'k': /* kilobytes 1<<10 */ scale = 0x400LL; break; case 'M': /* megabytes 1<<20 */ scale = 0x100000LL; break; case 'G': /* gigabytes 1<<30 */ scale = 0x40000000LL; break; case 'T': /* terabytes 1<<40 */ scale = 0x10000000000LL; break; case 'P': /* petabytes 1<<50 */ scale = 0x4000000000000LL; break; default: errx(1, "%s: %s: illegal trailing character", option->name, size_str); break; } if (new->o_data > QUAD_MAX / scale) errx(1, "%s: %s: value too large", option->name, size_str); new->o_data *= scale; } return new; } /* * -sparse functions -- * * Check if a file is sparse by finding if it occupies fewer blocks * than we expect based on its size. */ int f_sparse(PLAN *plan __unused, FTSENT *entry) { off_t expected_blocks; expected_blocks = (entry->fts_statp->st_size + 511) / 512; return entry->fts_statp->st_blocks < expected_blocks; } PLAN * c_sparse(OPTION *option, char ***argvp __unused) { ftsoptions &= ~FTS_NOSTAT; return palloc(option); } /* * -type c functions -- * * True if the type of the file is c, where c is b, c, d, p, f or w * for block special file, character special file, directory, FIFO, * regular file or whiteout respectively. */ int f_type(PLAN *plan, FTSENT *entry) { if (plan->m_data == S_IFDIR) return (entry->fts_info == FTS_D || entry->fts_info == FTS_DC || entry->fts_info == FTS_DNR || entry->fts_info == FTS_DOT || entry->fts_info == FTS_DP); else return (entry->fts_statp->st_mode & S_IFMT) == plan->m_data; } PLAN * c_type(OPTION *option, char ***argvp) { char *typestring; PLAN *new; mode_t mask; typestring = nextarg(option, argvp); if (typestring[0] != 'd') ftsoptions &= ~FTS_NOSTAT; switch (typestring[0]) { case 'b': mask = S_IFBLK; break; case 'c': mask = S_IFCHR; break; case 'd': mask = S_IFDIR; break; case 'f': mask = S_IFREG; break; case 'l': mask = S_IFLNK; break; case 'p': mask = S_IFIFO; break; case 's': mask = S_IFSOCK; break; #if defined(FTS_WHITEOUT) && defined(S_IFWHT) case 'w': mask = S_IFWHT; ftsoptions |= FTS_WHITEOUT; break; #endif /* FTS_WHITEOUT */ default: errx(1, "%s: %s: unknown type", option->name, typestring); } new = palloc(option); new->m_data = mask; return new; } /* * -user uname functions -- * * True if the file belongs to the user uname. If uname is numeric and * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not * return a valid user name, uname is taken as a user ID. */ int f_user(PLAN *plan, FTSENT *entry) { COMPARE(entry->fts_statp->st_uid, plan->u_data); } PLAN * c_user(OPTION *option, char ***argvp) { char *username; PLAN *new; struct passwd *p; uid_t uid; username = nextarg(option, argvp); ftsoptions &= ~FTS_NOSTAT; new = palloc(option); p = getpwnam(username); if (p == NULL) { char* cp = username; if( username[0] == '-' || username[0] == '+' ) username++; uid = atoi(username); if (uid == 0 && username[0] != '0') errx(1, "%s: %s: no such user", option->name, username); uid = find_parsenum(new, option->name, cp, NULL); } else uid = p->pw_uid; new->u_data = uid; return new; } /* * -xdev functions -- * * Always true, causes find not to descend past directories that have a * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) */ PLAN * c_xdev(OPTION *option, char ***argvp __unused) { ftsoptions |= FTS_XDEV; return palloc(option); } /* * ( expression ) functions -- * * True if expression is true. */ int f_expr(PLAN *plan, FTSENT *entry) { PLAN *p; int state = 0; for (p = plan->p_data[0]; p && (state = (p->execute)(p, entry)); p = p->next); return state; } /* * f_openparen and f_closeparen nodes are temporary place markers. They are * eliminated during phase 2 of find_formplan() --- the '(' node is converted * to a f_expr node containing the expression and the ')' node is discarded. * The functions themselves are only used as constants. */ int f_openparen(PLAN *plan __unused, FTSENT *entry __unused) { abort(); } int f_closeparen(PLAN *plan __unused, FTSENT *entry __unused) { abort(); } /* c_openparen == c_simple */ /* c_closeparen == c_simple */ /* * AND operator. Since AND is implicit, no node is allocated. */ PLAN * c_and(OPTION *option __unused, char ***argvp __unused) { return NULL; } /* * ! expression functions -- * * Negation of a primary; the unary NOT operator. */ int f_not(PLAN *plan, FTSENT *entry) { PLAN *p; int state = 0; for (p = plan->p_data[0]; p && (state = (p->execute)(p, entry)); p = p->next); return !state; } /* c_not == c_simple */ /* * expression -o expression functions -- * * Alternation of primaries; the OR operator. The second expression is * not evaluated if the first expression is true. */ int f_or(PLAN *plan, FTSENT *entry) { PLAN *p; int state = 0; for (p = plan->p_data[0]; p && (state = (p->execute)(p, entry)); p = p->next); if (state) return 1; for (p = plan->p_data[1]; p && (state = (p->execute)(p, entry)); p = p->next); return state; } /* c_or == c_simple */ /* * -false * * Always false. */ int f_false(PLAN *plan __unused, FTSENT *entry __unused) { return 0; } /* c_false == c_simple */ /* * -quit * * Exits the program */ int f_quit(PLAN *plan __unused, FTSENT *entry __unused) { finish_execplus(); exit(exitstatus); } /* c_quit == c_simple */ diff --git a/usr.bin/find/ls.c b/usr.bin/find/ls.c index a61d9cb7dbcd..86434f53e9fd 100644 --- a/usr.bin/find/ls.c +++ b/usr.bin/find/ls.c @@ -1,121 +1,119 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "find.h" /* Derived from the print routines in the ls(1) source code. */ static void printlink(char *); static void printtime(time_t); void printlong(char *name, char *accpath, struct stat *sb) { char modep[15]; (void)printf("%6ju %8"PRId64" ", (uintmax_t)sb->st_ino, sb->st_blocks); (void)strmode(sb->st_mode, modep); (void)printf("%s %3ju %-*s %-*s ", modep, (uintmax_t)sb->st_nlink, MAXLOGNAME - 1, user_from_uid(sb->st_uid, 0), MAXLOGNAME - 1, group_from_gid(sb->st_gid, 0)); if (S_ISCHR(sb->st_mode) || S_ISBLK(sb->st_mode)) (void)printf("%#8jx ", (uintmax_t)sb->st_rdev); else (void)printf("%8"PRId64" ", sb->st_size); printtime(sb->st_mtime); (void)printf("%s", name); if (S_ISLNK(sb->st_mode)) printlink(accpath); (void)putchar('\n'); } static void printtime(time_t ftime) { char longstring[80]; static time_t lnow; const char *format; static int d_first = -1; struct tm *tm; #ifdef D_MD_ORDER if (d_first < 0) d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); #endif if (lnow == 0) lnow = time(NULL); #define SIXMONTHS ((365 / 2) * 86400) if (ftime + SIXMONTHS > lnow && ftime < lnow + SIXMONTHS) /* mmm dd hh:mm || dd mmm hh:mm */ format = d_first ? "%e %b %R " : "%b %e %R "; else /* mmm dd yyyy || dd mmm yyyy */ format = d_first ? "%e %b %Y " : "%b %e %Y "; if ((tm = localtime(&ftime)) != NULL) strftime(longstring, sizeof(longstring), format, tm); else strlcpy(longstring, "bad date val ", sizeof(longstring)); fputs(longstring, stdout); } static void printlink(char *name) { ssize_t lnklen; char path[MAXPATHLEN]; if ((lnklen = readlink(name, path, MAXPATHLEN - 1)) == -1) { warn("%s", name); return; } path[lnklen] = '\0'; (void)printf(" -> %s", path); } diff --git a/usr.bin/find/misc.c b/usr.bin/find/misc.c index 01c9f9145fa2..7963e3baa4d3 100644 --- a/usr.bin/find/misc.c +++ b/usr.bin/find/misc.c @@ -1,109 +1,107 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Cimarron D. Taylor of the University of California, Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - -#include #include #include #include #include #include #include #include #include #include "find.h" /* * brace_subst -- * Replace occurrences of {} in s1 with s2 and return the result string. */ void brace_subst(char *orig, char **store, char *path, size_t len) { const char *pastorigend, *p, *q; char *dst; size_t newlen, plen; plen = strlen(path); newlen = strlen(orig) + 1; pastorigend = orig + newlen; for (p = orig; (q = strstr(p, "{}")) != NULL; p = q + 2) { if (plen > 2 && newlen + plen - 2 < newlen) errx(2, "brace_subst overflow"); newlen += plen - 2; } if (newlen > len) { *store = reallocf(*store, newlen); if (*store == NULL) err(2, NULL); } dst = *store; for (p = orig; (q = strstr(p, "{}")) != NULL; p = q + 2) { memcpy(dst, p, q - p); dst += q - p; memcpy(dst, path, plen); dst += plen; } memcpy(dst, p, pastorigend - p); } /* * queryuser -- * print a message to standard error and then read input from standard * input. If the input is an affirmative response (according to the * current locale) then 1 is returned. */ int queryuser(char *argv[]) { char *p, resp[256]; (void)fprintf(stderr, "\"%s", *argv); while (*++argv) (void)fprintf(stderr, " %s", *argv); (void)fprintf(stderr, "\"? "); (void)fflush(stderr); if (fgets(resp, sizeof(resp), stdin) == NULL) *resp = '\0'; if ((p = strchr(resp, '\n')) != NULL) *p = '\0'; else { (void)fprintf(stderr, "\n"); (void)fflush(stderr); } return (rpmatch(resp) == 1); } diff --git a/usr.bin/find/operator.c b/usr.bin/find/operator.c index f65b800c32a6..3b4936fe055b 100644 --- a/usr.bin/find/operator.c +++ b/usr.bin/find/operator.c @@ -1,269 +1,267 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Cimarron D. Taylor of the University of California, Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - -#include #include #include #include #include #include #include "find.h" static PLAN *yanknode(PLAN **); static PLAN *yankexpr(PLAN **); /* * yanknode -- * destructively removes the top from the plan */ static PLAN * yanknode(PLAN **planp) { PLAN *node; /* top node removed from the plan */ if ((node = (*planp)) == NULL) return (NULL); (*planp) = (*planp)->next; node->next = NULL; return (node); } /* * yankexpr -- * Removes one expression from the plan. This is used mainly by * paren_squish. In comments below, an expression is either a * simple node or a f_expr node containing a list of simple nodes. */ static PLAN * yankexpr(PLAN **planp) { PLAN *next; /* temp node holding subexpression results */ PLAN *node; /* pointer to returned node or expression */ PLAN *tail; /* pointer to tail of subplan */ PLAN *subplan; /* pointer to head of ( ) expression */ /* first pull the top node from the plan */ if ((node = yanknode(planp)) == NULL) return (NULL); /* * If the node is an '(' then we recursively slurp up expressions * until we find its associated ')'. If it's a closing paren we * just return it and unwind our recursion; all other nodes are * complete expressions, so just return them. */ if (node->execute == f_openparen) for (tail = subplan = NULL;;) { if ((next = yankexpr(planp)) == NULL) errx(1, "(: missing closing ')'"); /* * If we find a closing ')' we store the collected * subplan in our '(' node and convert the node to * a f_expr. The ')' we found is ignored. Otherwise, * we just continue to add whatever we get to our * subplan. */ if (next->execute == f_closeparen) { if (subplan == NULL) errx(1, "(): empty inner expression"); node->p_data[0] = subplan; node->execute = f_expr; break; } else { if (subplan == NULL) tail = subplan = next; else { tail->next = next; tail = next; } tail->next = NULL; } } return (node); } /* * paren_squish -- * replaces "parenthesized" plans in our search plan with "expr" nodes. */ PLAN * paren_squish(PLAN *plan) { PLAN *expr; /* pointer to next expression */ PLAN *tail; /* pointer to tail of result plan */ PLAN *result; /* pointer to head of result plan */ result = tail = NULL; /* * the basic idea is to have yankexpr do all our work and just * collect its results together. */ while ((expr = yankexpr(&plan)) != NULL) { /* * if we find an unclaimed ')' it means there is a missing * '(' someplace. */ if (expr->execute == f_closeparen) errx(1, "): no beginning '('"); /* add the expression to our result plan */ if (result == NULL) tail = result = expr; else { tail->next = expr; tail = expr; } tail->next = NULL; } return (result); } /* * not_squish -- * compresses "!" expressions in our search plan. */ PLAN * not_squish(PLAN *plan) { PLAN *next; /* next node being processed */ PLAN *node; /* temporary node used in f_not processing */ PLAN *tail; /* pointer to tail of result plan */ PLAN *result; /* pointer to head of result plan */ tail = result = NULL; while ((next = yanknode(&plan))) { /* * if we encounter a ( expression ) then look for nots in * the expr subplan. */ if (next->execute == f_expr) next->p_data[0] = not_squish(next->p_data[0]); /* * if we encounter a not, then snag the next node and place * it in the not's subplan. As an optimization we compress * several not's to zero or one not. */ if (next->execute == f_not) { int notlevel = 1; node = yanknode(&plan); while (node != NULL && node->execute == f_not) { ++notlevel; node = yanknode(&plan); } if (node == NULL) errx(1, "!: no following expression"); if (node->execute == f_or) errx(1, "!: nothing between ! and -o"); /* * If we encounter ! ( expr ) then look for nots in * the expr subplan. */ if (node->execute == f_expr) node->p_data[0] = not_squish(node->p_data[0]); if (notlevel % 2 != 1) next = node; else next->p_data[0] = node; } /* add the node to our result plan */ if (result == NULL) tail = result = next; else { tail->next = next; tail = next; } tail->next = NULL; } return (result); } /* * or_squish -- * compresses -o expressions in our search plan. */ PLAN * or_squish(PLAN *plan) { PLAN *next; /* next node being processed */ PLAN *tail; /* pointer to tail of result plan */ PLAN *result; /* pointer to head of result plan */ tail = result = next = NULL; while ((next = yanknode(&plan)) != NULL) { /* * if we encounter a ( expression ) then look for or's in * the expr subplan. */ if (next->execute == f_expr) next->p_data[0] = or_squish(next->p_data[0]); /* if we encounter a not then look for or's in the subplan */ if (next->execute == f_not) next->p_data[0] = or_squish(next->p_data[0]); /* * if we encounter an or, then place our collected plan in the * or's first subplan and then recursively collect the * remaining stuff into the second subplan and return the or. */ if (next->execute == f_or) { if (result == NULL) errx(1, "-o: no expression before -o"); next->p_data[0] = result; next->p_data[1] = or_squish(plan); if (next->p_data[1] == NULL) errx(1, "-o: no expression after -o"); return (next); } /* add the node to our result plan */ if (result == NULL) tail = result = next; else { tail->next = next; tail = next; } tail->next = NULL; } return (result); } diff --git a/usr.bin/find/option.c b/usr.bin/find/option.c index 4af5af94e329..166fc75e9c51 100644 --- a/usr.bin/find/option.c +++ b/usr.bin/find/option.c @@ -1,206 +1,205 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Cimarron D. Taylor of the University of California, Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include "find.h" static int typecompare(const void *, const void *); /* NB: the following table must be sorted lexically. */ /* Options listed with C++ comments are in gnu find, but not our find */ static OPTION const options[] = { { "!", c_simple, f_not, 0 }, { "(", c_simple, f_openparen, 0 }, { ")", c_simple, f_closeparen, 0 }, #if HAVE_STRUCT_STAT_ST_BIRTHTIME { "-Bmin", c_Xmin, f_Xmin, F_TIME_B }, { "-Bnewer", c_newer, f_newer, F_TIME_B }, { "-Btime", c_Xtime, f_Xtime, F_TIME_B }, #endif { "-a", c_and, NULL, 0 }, #ifdef ACL_TYPE_NFS4 { "-acl", c_acl, f_acl, 0 }, #endif { "-amin", c_Xmin, f_Xmin, F_TIME_A }, { "-and", c_and, NULL, 0 }, { "-anewer", c_newer, f_newer, F_TIME_A }, { "-atime", c_Xtime, f_Xtime, F_TIME_A }, { "-cmin", c_Xmin, f_Xmin, F_TIME_C }, { "-cnewer", c_newer, f_newer, F_TIME_C }, { "-ctime", c_Xtime, f_Xtime, F_TIME_C }, { "-d", c_depth, f_depth, 0 }, // -daystart { "-delete", c_delete, f_delete, 0 }, { "-depth", c_depth, f_depth, 0 }, { "-empty", c_empty, f_empty, 0 }, { "-exec", c_exec, f_exec, 0 }, { "-execdir", c_exec, f_exec, F_EXECDIR }, { "-false", c_simple, f_false, 0 }, #if HAVE_STRUCT_STAT_ST_FLAGS { "-flags", c_flags, f_flags, 0 }, #endif // -fls { "-follow", c_follow, f_always_true, 0 }, // -fprint // -fprint0 // -fprintf #if HAVE_STRUCT_STATFS_F_FSTYPENAME { "-fstype", c_fstype, f_fstype, 0 }, #endif { "-gid", c_group, f_group, 0 }, { "-group", c_group, f_group, 0 }, { "-ignore_readdir_race",c_ignore_readdir_race, f_always_true,0 }, { "-ilname", c_name, f_name, F_LINK | F_IGNCASE }, { "-iname", c_name, f_name, F_IGNCASE }, { "-inum", c_inum, f_inum, 0 }, { "-ipath", c_name, f_path, F_IGNCASE }, { "-iregex", c_regex, f_regex, F_IGNCASE }, { "-iwholename",c_name, f_path, F_IGNCASE }, { "-links", c_links, f_links, 0 }, { "-lname", c_name, f_name, F_LINK }, { "-ls", c_ls, f_ls, 0 }, { "-maxdepth", c_mXXdepth, f_always_true, F_MAXDEPTH }, { "-mindepth", c_mXXdepth, f_always_true, 0 }, { "-mmin", c_Xmin, f_Xmin, 0 }, { "-mnewer", c_newer, f_newer, 0 }, { "-mount", c_xdev, f_always_true, 0 }, { "-mtime", c_Xtime, f_Xtime, 0 }, { "-name", c_name, f_name, 0 }, { "-newer", c_newer, f_newer, 0 }, #if HAVE_STRUCT_STAT_ST_BIRTHTIME { "-newerBB", c_newer, f_newer, F_TIME_B | F_TIME2_B }, { "-newerBa", c_newer, f_newer, F_TIME_B | F_TIME2_A }, { "-newerBc", c_newer, f_newer, F_TIME_B | F_TIME2_C }, { "-newerBm", c_newer, f_newer, F_TIME_B }, { "-newerBt", c_newer, f_newer, F_TIME_B | F_TIME2_T }, { "-neweraB", c_newer, f_newer, F_TIME_A | F_TIME2_B }, #endif { "-neweraa", c_newer, f_newer, F_TIME_A | F_TIME2_A }, { "-newerac", c_newer, f_newer, F_TIME_A | F_TIME2_C }, { "-neweram", c_newer, f_newer, F_TIME_A }, { "-newerat", c_newer, f_newer, F_TIME_A | F_TIME2_T }, #if HAVE_STRUCT_STAT_ST_BIRTHTIME { "-newercB", c_newer, f_newer, F_TIME_C | F_TIME2_B }, #endif { "-newerca", c_newer, f_newer, F_TIME_C | F_TIME2_A }, { "-newercc", c_newer, f_newer, F_TIME_C | F_TIME2_C }, { "-newercm", c_newer, f_newer, F_TIME_C }, { "-newerct", c_newer, f_newer, F_TIME_C | F_TIME2_T }, #if HAVE_STRUCT_STAT_ST_BIRTHTIME { "-newermB", c_newer, f_newer, F_TIME2_B }, #endif { "-newerma", c_newer, f_newer, F_TIME2_A }, { "-newermc", c_newer, f_newer, F_TIME2_C }, { "-newermm", c_newer, f_newer, 0 }, { "-newermt", c_newer, f_newer, F_TIME2_T }, { "-nogroup", c_nogroup, f_nogroup, 0 }, { "-noignore_readdir_race",c_ignore_readdir_race, f_always_true,0 }, { "-noleaf", c_simple, f_always_true, 0 }, { "-not", c_simple, f_not, 0 }, { "-nouser", c_nouser, f_nouser, 0 }, { "-o", c_simple, f_or, 0 }, { "-ok", c_exec, f_exec, F_NEEDOK }, { "-okdir", c_exec, f_exec, F_NEEDOK | F_EXECDIR }, { "-or", c_simple, f_or, 0 }, { "-path", c_name, f_path, 0 }, { "-perm", c_perm, f_perm, 0 }, { "-print", c_print, f_print, 0 }, { "-print0", c_print, f_print0, 0 }, // -printf { "-prune", c_simple, f_prune, 0 }, { "-quit", c_simple, f_quit, 0 }, { "-regex", c_regex, f_regex, 0 }, { "-samefile", c_samefile, f_inum, 0 }, { "-size", c_size, f_size, 0 }, { "-sparse", c_sparse, f_sparse, 0 }, { "-true", c_simple, f_always_true, 0 }, { "-type", c_type, f_type, 0 }, { "-uid", c_user, f_user, 0 }, { "-user", c_user, f_user, 0 }, { "-wholename", c_name, f_path, 0 }, { "-xdev", c_xdev, f_always_true, 0 }, // -xtype }; /* * find_create -- * create a node corresponding to a command line argument. * * TODO: * add create/process function pointers to node, so we can skip * this switch stuff. */ PLAN * find_create(char ***argvp) { OPTION *p; PLAN *new; char **argv; argv = *argvp; if ((p = lookup_option(*argv)) == NULL) errx(1, "%s: unknown primary or operator", *argv); ++argv; new = (p->create)(p, &argv); *argvp = argv; return (new); } OPTION * lookup_option(const char *name) { OPTION tmp; tmp.name = name; return ((OPTION *)bsearch(&tmp, options, sizeof(options)/sizeof(OPTION), sizeof(OPTION), typecompare)); } static int typecompare(const void *a, const void *b) { return (strcmp(((const OPTION *)a)->name, ((const OPTION *)b)->name)); } diff --git a/usr.bin/finger/lprint.c b/usr.bin/finger/lprint.c index 11491e5a7c8e..6d5e828eb274 100644 --- a/usr.bin/finger/lprint.c +++ b/usr.bin/finger/lprint.c @@ -1,360 +1,356 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Tony Nardo of the Johns Hopkins University/Applied Physics Lab. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "finger.h" #include "pathnames.h" #define LINE_LEN 80 #define TAB_LEN 8 /* 8 spaces between tabs */ static int demi_print(char *, int); static void lprint(PERSON *); static void vputc(unsigned char); void lflag_print(void) { PERSON *pn; int sflag, r; PERSON *tmp; DBT data, key; for (sflag = R_FIRST;; sflag = R_NEXT) { r = (*db->seq)(db, &key, &data, sflag); if (r == -1) err(1, "db seq"); if (r == 1) break; memmove(&tmp, data.data, sizeof tmp); pn = tmp; if (sflag != R_FIRST) putchar('\n'); lprint(pn); if (!pplan) { (void)show_text(pn->dir, _PATH_FORWARD, "Mail forwarded to"); (void)show_text(pn->dir, _PATH_PROJECT, "Project"); if (!show_text(pn->dir, _PATH_PLAN, "Plan")) (void)printf("No Plan.\n"); (void)show_text(pn->dir, _PATH_PUBKEY, "Public key"); } } } static void lprint(PERSON *pn) { struct tm *delta; WHERE *w; int cpr, len, maxlen; struct tm *tp; int oddfield; char t[80]; if (d_first < 0) d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); /* * long format -- * login name * real name * home directory * shell * office, office phone, home phone if available * mail status */ (void)printf("Login: %-15s\t\t\tName: %s\nDirectory: %-25s", pn->name, pn->realname, pn->dir); (void)printf("\tShell: %-s\n", *pn->shell ? pn->shell : _PATH_BSHELL); if (gflag) goto no_gecos; /* * try and print office, office phone, and home phone on one line; * if that fails, do line filling so it looks nice. */ #define OFFICE_TAG "Office" #define OFFICE_PHONE_TAG "Office Phone" oddfield = 0; if (pn->office && pn->officephone && strlen(pn->office) + strlen(pn->officephone) + sizeof(OFFICE_TAG) + 2 <= 5 * TAB_LEN) { (void)snprintf(tbuf, sizeof(tbuf), "%s: %s, %s", OFFICE_TAG, pn->office, prphone(pn->officephone)); oddfield = demi_print(tbuf, oddfield); } else { if (pn->office) { (void)snprintf(tbuf, sizeof(tbuf), "%s: %s", OFFICE_TAG, pn->office); oddfield = demi_print(tbuf, oddfield); } if (pn->officephone) { (void)snprintf(tbuf, sizeof(tbuf), "%s: %s", OFFICE_PHONE_TAG, prphone(pn->officephone)); oddfield = demi_print(tbuf, oddfield); } } if (pn->homephone) { (void)snprintf(tbuf, sizeof(tbuf), "%s: %s", "Home Phone", prphone(pn->homephone)); oddfield = demi_print(tbuf, oddfield); } if (oddfield) putchar('\n'); no_gecos: /* * long format con't: * if logged in * terminal * idle time * if messages allowed * where logged in from * if not logged in * when last logged in */ /* find out longest device name for this user for formatting */ for (w = pn->whead, maxlen = -1; w != NULL; w = w->next) if ((len = strlen(w->tty)) > maxlen) maxlen = len; /* find rest of entries for user */ for (w = pn->whead; w != NULL; w = w->next) { if (w->info == LOGGEDIN) { tp = localtime(&w->loginat); strftime(t, sizeof(t), d_first ? "%a %e %b %R (%Z)" : "%a %b %e %R (%Z)", tp); cpr = printf("On since %s on %s", t, w->tty); /* * idle time is tough; if have one, print a comma, * then spaces to pad out the device name, then the * idle time. Follow with a comma if a remote login. */ delta = gmtime(&w->idletime); if (w->idletime != -1 && (delta->tm_yday || delta->tm_hour || delta->tm_min)) { cpr += printf("%-*s idle ", maxlen - (int)strlen(w->tty) + 1, ","); if (delta->tm_yday > 0) { cpr += printf("%d day%s ", delta->tm_yday, delta->tm_yday == 1 ? "" : "s"); } cpr += printf("%d:%02d", delta->tm_hour, delta->tm_min); if (*w->host) { putchar(','); ++cpr; } } if (!w->writable) cpr += printf(" (messages off)"); } else if (w->loginat == 0) { cpr = printf("Never logged in."); } else { tp = localtime(&w->loginat); if (now - w->loginat > 86400 * 365 / 2) { strftime(t, sizeof(t), d_first ? "%a %e %b %R %Y (%Z)" : "%a %b %e %R %Y (%Z)", tp); } else { strftime(t, sizeof(t), d_first ? "%a %e %b %R (%Z)" : "%a %b %e %R (%Z)", tp); } cpr = printf("Last login %s on %s", t, w->tty); } if (*w->host) { if (LINE_LEN < (cpr + 6 + strlen(w->host))) (void)printf("\n "); (void)printf(" from %s", w->host); } putchar('\n'); } if (pn->mailrecv == -1) printf("No Mail.\n"); else if (pn->mailrecv > pn->mailread) { tp = localtime(&pn->mailrecv); strftime(t, sizeof(t), d_first ? "%a %e %b %R %Y (%Z)" : "%a %b %e %R %Y (%Z)", tp); printf("New mail received %s\n", t); tp = localtime(&pn->mailread); strftime(t, sizeof(t), d_first ? "%a %e %b %R %Y (%Z)" : "%a %b %e %R %Y (%Z)", tp); printf(" Unread since %s\n", t); } else { tp = localtime(&pn->mailread); strftime(t, sizeof(t), d_first ? "%a %e %b %R %Y (%Z)" : "%a %b %e %R %Y (%Z)", tp); printf("Mail last read %s\n", t); } } static int demi_print(char *str, int oddfield) { static int lenlast; int lenthis, maxlen; lenthis = strlen(str); if (oddfield) { /* * We left off on an odd number of fields. If we haven't * crossed the midpoint of the screen, and we have room for * the next field, print it on the same line; otherwise, * print it on a new line. * * Note: we insist on having the right hand fields start * no less than 5 tabs out. */ maxlen = 5 * TAB_LEN; if (maxlen < lenlast) maxlen = lenlast; if (((((maxlen / TAB_LEN) + 1) * TAB_LEN) + lenthis) <= LINE_LEN) { while(lenlast < (4 * TAB_LEN)) { putchar('\t'); lenlast += TAB_LEN; } (void)printf("\t%s\n", str); /* force one tab */ } else { (void)printf("\n%s", str); /* go to next line */ oddfield = !oddfield; /* this'll be undone below */ } } else (void)printf("%s", str); oddfield = !oddfield; /* toggle odd/even marker */ lenlast = lenthis; return(oddfield); } int show_text(const char *directory, const char *file_name, const char *header) { struct stat sb; FILE *fp; int ch, cnt; char *p, lastc; int fd, nr; lastc = '\0'; (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", directory, file_name); if ((fd = open(tbuf, O_RDONLY)) < 0 || fstat(fd, &sb) || sb.st_size == 0) return(0); /* If short enough, and no newlines, show it on a single line.*/ if (sb.st_size <= (off_t)(LINE_LEN - strlen(header) - 5)) { nr = read(fd, tbuf, sizeof(tbuf)); if (nr <= 0) { (void)close(fd); return(0); } for (p = tbuf, cnt = nr; cnt--; ++p) if (*p == '\n') break; if (cnt <= 1) { if (*header != '\0') (void)printf("%s: ", header); for (p = tbuf, cnt = nr; cnt--; ++p) if (*p != '\r') vputc(lastc = *p); if (lastc != '\n') (void)putchar('\n'); (void)close(fd); return(1); } else (void)lseek(fd, 0L, SEEK_SET); } if ((fp = fdopen(fd, "r")) == NULL) return(0); if (*header != '\0') (void)printf("%s:\n", header); while ((ch = getc(fp)) != EOF) if (ch != '\r') vputc(lastc = ch); if (lastc != '\n') (void)putchar('\n'); (void)fclose(fp); return(1); } static void vputc(unsigned char ch) { int meta; if (!isprint(ch) && !isascii(ch)) { (void)putchar('M'); (void)putchar('-'); ch = toascii(ch); meta = 1; } else meta = 0; if (isprint(ch) || (!meta && (ch == ' ' || ch == '\t' || ch == '\n'))) (void)putchar(ch); else { (void)putchar('^'); (void)putchar(ch == '\177' ? '?' : ch | 0100); } } diff --git a/usr.bin/finger/net.c b/usr.bin/finger/net.c index 59356cd09e1f..e2f1eb99d623 100644 --- a/usr.bin/finger/net.c +++ b/usr.bin/finger/net.c @@ -1,234 +1,230 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Tony Nardo of the Johns Hopkins University/Applied Physics Lab. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "finger.h" static void cleanup(int sig); static int do_protocol(const char *name, const struct addrinfo *ai); static void trying(const struct addrinfo *ai); void netfinger(char *name) { int error, multi; char *host; struct addrinfo *ai, *ai0; static struct addrinfo hint; host = strrchr(name, '@'); if (host == NULL) return; *host++ = '\0'; signal(SIGALRM, cleanup); alarm(TIME_LIMIT); hint.ai_flags = AI_CANONNAME; hint.ai_family = family; hint.ai_socktype = SOCK_STREAM; error = getaddrinfo(host, "finger", &hint, &ai0); if (error) { warnx("%s: %s", host, gai_strerror(error)); return; } multi = (ai0->ai_next) != 0; /* ai_canonname may not be filled in if the user specified an IP. */ if (ai0->ai_canonname == 0) printf("[%s]\n", host); else printf("[%s]\n", ai0->ai_canonname); for (ai = ai0; ai != NULL; ai = ai->ai_next) { if (multi) trying(ai); error = do_protocol(name, ai); if (!error) break; } alarm(0); freeaddrinfo(ai0); } static int do_protocol(const char *name, const struct addrinfo *ai) { int cnt, line_len, s; FILE *fp; wint_t c, lastc; struct iovec iov[3]; struct msghdr msg; static char slash_w[] = "/W "; static char neteol[] = "\r\n"; s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (s < 0) { warn("socket(%d, %d, %d)", ai->ai_family, ai->ai_socktype, ai->ai_protocol); return -1; } msg.msg_name = (void *)ai->ai_addr; msg.msg_namelen = ai->ai_addrlen; msg.msg_iov = iov; msg.msg_iovlen = 0; msg.msg_control = 0; msg.msg_controllen = 0; msg.msg_flags = 0; /* -l flag for remote fingerd */ if (lflag) { iov[msg.msg_iovlen].iov_base = slash_w; iov[msg.msg_iovlen++].iov_len = 3; } /* send the name followed by */ iov[msg.msg_iovlen].iov_base = strdup(name); iov[msg.msg_iovlen++].iov_len = strlen(name); iov[msg.msg_iovlen].iov_base = neteol; iov[msg.msg_iovlen++].iov_len = 2; if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) { warn("connect"); close(s); return -1; } if (sendmsg(s, &msg, 0) < 0) { warn("sendmsg"); close(s); return -1; } /* * Read from the remote system; once we're connected, we assume some * data. If none arrives, we hang until the user interrupts. * * If we see a or a with the high bit set, treat it as * a newline; if followed by a newline character, only output one * newline. * * Otherwise, all high bits are stripped; if it isn't printable and * it isn't a space, we can simply set the 7th bit. Every ASCII * character with bit 7 set is printable. */ lastc = 0; if ((fp = fdopen(s, "r")) != NULL) { cnt = 0; line_len = 0; while ((c = getwc(fp)) != EOF) { if (++cnt > OUTPUT_MAX) { printf("\n\n Output truncated at %d bytes...\n", cnt - 1); break; } if (c == 0x0d) { if (lastc == '\r') /* ^M^M - skip dupes */ continue; c = '\n'; lastc = '\r'; } else { if (!iswprint(c) && !iswspace(c)) { c &= 0x7f; c |= 0x40; } if (lastc != '\r' || c != '\n') lastc = c; else { lastc = '\n'; continue; } } putwchar(c); if (c != '\n' && ++line_len > _POSIX2_LINE_MAX) { putchar('\\'); putchar('\n'); lastc = '\r'; } if (lastc == '\n' || lastc == '\r') line_len = 0; } if (ferror(fp)) { /* * Assume that whatever it was set errno... */ warn("reading from network"); } if (lastc != L'\n') putchar('\n'); fclose(fp); } return 0; } static void trying(const struct addrinfo *ai) { char buf[NI_MAXHOST]; if (getnameinfo(ai->ai_addr, ai->ai_addrlen, buf, sizeof buf, (char *)0, 0, NI_NUMERICHOST) != 0) return; /* XXX can't happen */ printf("Trying %s...\n", buf); } static void cleanup(int sig __unused) { #define ERRSTR "Timed out.\n" write(STDERR_FILENO, ERRSTR, sizeof ERRSTR); exit(1); } diff --git a/usr.bin/finger/sprint.c b/usr.bin/finger/sprint.c index 1d65b23ae073..d8649a1f57f6 100644 --- a/usr.bin/finger/sprint.c +++ b/usr.bin/finger/sprint.c @@ -1,181 +1,177 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Tony Nardo of the Johns Hopkins University/Applied Physics Lab. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include #include "finger.h" static void stimeprint(WHERE *); void sflag_print(void) { PERSON *pn; WHERE *w; int sflag, r, namelen; char p[80]; PERSON *tmp; DBT data, key; struct tm *lc; if (d_first < 0) d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); /* * short format -- * login name * real name * terminal name (the XX of ttyXX) * if terminal writeable (add an '*' to the terminal name * if not) * if logged in show idle time and day logged in, else * show last login date and time. * If > 6 months, show year instead of time. * if (-o) * office location * office phone * else * remote host */ #define MAXREALNAME 16 #define MAXHOSTNAME 17 /* in reality, hosts are never longer than 16 */ (void)printf("%-*s %-*s%s %s\n", MAXLOGNAME, "Login", MAXREALNAME, "Name", " TTY Idle Login Time ", (gflag) ? "" : oflag ? "Office Phone" : "Where"); for (sflag = R_FIRST;; sflag = R_NEXT) { r = (*db->seq)(db, &key, &data, sflag); if (r == -1) err(1, "db seq"); if (r == 1) break; memmove(&tmp, data.data, sizeof tmp); pn = tmp; for (w = pn->whead; w != NULL; w = w->next) { namelen = MAXREALNAME; if (w->info == LOGGEDIN && !w->writable) --namelen; /* leave space before `*' */ (void)printf("%-*.*s %-*.*s", MAXLOGNAME, MAXLOGNAME, pn->name, MAXREALNAME, namelen, pn->realname ? pn->realname : ""); if (!w->loginat) { (void)printf(" * * No logins "); goto office; } (void)putchar(w->info == LOGGEDIN && !w->writable ? '*' : ' '); if (*w->tty) (void)printf("%-7.7s ", (strncmp(w->tty, "tty", 3) && strncmp(w->tty, "cua", 3)) ? w->tty : w->tty + 3); else (void)printf(" "); if (w->info == LOGGEDIN) { stimeprint(w); (void)printf(" "); } else (void)printf(" * "); lc = localtime(&w->loginat); #define SECSPERDAY 86400 #define DAYSPERWEEK 7 #define DAYSPERNYEAR 365 if (now - w->loginat < SECSPERDAY * (DAYSPERWEEK - 1)) { (void)strftime(p, sizeof(p), "%a", lc); } else { (void)strftime(p, sizeof(p), d_first ? "%e %b" : "%b %e", lc); } (void)printf("%-6.6s", p); if (now - w->loginat >= SECSPERDAY * DAYSPERNYEAR / 2) { (void)strftime(p, sizeof(p), "%Y", lc); } else { (void)strftime(p, sizeof(p), "%R", lc); } (void)printf(" %-5.5s", p); office: if (gflag) goto no_gecos; if (oflag) { if (pn->office) (void)printf(" %-7.7s", pn->office); else if (pn->officephone) (void)printf(" %-7.7s", " "); if (pn->officephone) (void)printf(" %-.15s", prphone(pn->officephone)); } else (void)printf(" %.*s", MAXHOSTNAME, w->host); no_gecos: putchar('\n'); } } } static void stimeprint(WHERE *w) { struct tm *delta; if (w->idletime == -1) { (void)printf(" "); return; } delta = gmtime(&w->idletime); if (!delta->tm_yday) if (!delta->tm_hour) if (!delta->tm_min) (void)printf(" "); else (void)printf("%5d", delta->tm_min); else (void)printf("%2d:%02d", delta->tm_hour, delta->tm_min); else (void)printf("%4dd", delta->tm_yday); } diff --git a/usr.bin/finger/util.c b/usr.bin/finger/util.c index 621f3455e82e..183e46290013 100644 --- a/usr.bin/finger/util.c +++ b/usr.bin/finger/util.c @@ -1,402 +1,398 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Tony Nardo of the Johns Hopkins University/Applied Physics Lab. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "finger.h" #include "pathnames.h" static void find_idle_and_ttywrite(WHERE *); static void userinfo(PERSON *, struct passwd *); static WHERE *walloc(PERSON *); int match(struct passwd *pw, const char *user) { char *p, *t; char name[1024]; if (!strcasecmp(pw->pw_name, user)) return(1); /* * XXX * Why do we skip asterisks!?!? */ (void)strncpy(p = tbuf, pw->pw_gecos, sizeof(tbuf)); tbuf[sizeof(tbuf) - 1] = '\0'; if (*p == '*') ++p; /* Ampersands get replaced by the login name. */ if ((p = strtok(p, ",")) == NULL) return(0); for (t = name; t < &name[sizeof(name) - 1] && (*t = *p) != '\0'; ++p) { if (*t == '&') { (void)strncpy(t, pw->pw_name, sizeof(name) - (t - name)); name[sizeof(name) - 1] = '\0'; while (t < &name[sizeof(name) - 1] && *++t) continue; } else { ++t; } } *t = '\0'; for (t = name; (p = strtok(t, "\t ")) != NULL; t = NULL) if (!strcasecmp(p, user)) return(1); return(0); } void enter_lastlog(PERSON *pn) { WHERE *w; struct utmpx *ut = NULL; char doit = 0; if (setutxdb(UTXDB_LASTLOGIN, NULL) == 0) ut = getutxuser(pn->name); if ((w = pn->whead) == NULL) doit = 1; else if (ut != NULL && ut->ut_type == USER_PROCESS) { /* if last login is earlier than some current login */ for (; !doit && w != NULL; w = w->next) if (w->info == LOGGEDIN && w->loginat < ut->ut_tv.tv_sec) doit = 1; /* * and if it's not any of the current logins * can't use time comparison because there may be a small * discrepancy since login calls time() twice */ for (w = pn->whead; doit && w != NULL; w = w->next) if (w->info == LOGGEDIN && strcmp(w->tty, ut->ut_line) == 0) doit = 0; } if (ut != NULL && doit) { w = walloc(pn); w->info = LASTLOG; strcpy(w->tty, ut->ut_line); strcpy(w->host, ut->ut_host); w->loginat = ut->ut_tv.tv_sec; } endutxent(); } void enter_where(struct utmpx *ut, PERSON *pn) { WHERE *w; w = walloc(pn); w->info = LOGGEDIN; strcpy(w->tty, ut->ut_line); strcpy(w->host, ut->ut_host); w->loginat = ut->ut_tv.tv_sec; find_idle_and_ttywrite(w); } PERSON * enter_person(struct passwd *pw) { DBT data, key; PERSON *pn; if (db == NULL && (db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL)) == NULL) err(1, NULL); key.data = pw->pw_name; key.size = strlen(pw->pw_name); switch ((*db->get)(db, &key, &data, 0)) { case 0: memmove(&pn, data.data, sizeof pn); return (pn); default: case -1: err(1, "db get"); /* NOTREACHED */ case 1: ++entries; pn = palloc(); userinfo(pn, pw); pn->whead = NULL; data.size = sizeof(PERSON *); data.data = &pn; if ((*db->put)(db, &key, &data, 0)) err(1, "db put"); return (pn); } } PERSON * find_person(char *name) { struct passwd *pw; DBT data, key; PERSON *p; if (!db) return(NULL); if ((pw = getpwnam(name)) && hide(pw)) return(NULL); key.data = name; key.size = strlen(name); if ((*db->get)(db, &key, &data, 0)) return (NULL); memmove(&p, data.data, sizeof p); return (p); } PERSON * palloc(void) { PERSON *p; if ((p = malloc(sizeof(PERSON))) == NULL) err(1, NULL); return(p); } static WHERE * walloc(PERSON *pn) { WHERE *w; if ((w = malloc(sizeof(WHERE))) == NULL) err(1, NULL); if (pn->whead == NULL) pn->whead = pn->wtail = w; else { pn->wtail->next = w; pn->wtail = w; } w->next = NULL; return(w); } char * prphone(char *num) { char *p; int len; static char pbuf[20]; /* don't touch anything if the user has their own formatting */ for (p = num; *p; ++p) if (!isdigit(*p)) return(num); len = p - num; p = pbuf; switch(len) { case 11: /* +0-123-456-7890 */ *p++ = '+'; *p++ = *num++; *p++ = '-'; /* FALLTHROUGH */ case 10: /* 012-345-6789 */ *p++ = *num++; *p++ = *num++; *p++ = *num++; *p++ = '-'; /* FALLTHROUGH */ case 7: /* 012-3456 */ *p++ = *num++; *p++ = *num++; *p++ = *num++; break; case 5: /* x0-1234 */ case 4: /* x1234 */ *p++ = 'x'; *p++ = *num++; break; default: return(num); } if (len != 4) { *p++ = '-'; *p++ = *num++; } *p++ = *num++; *p++ = *num++; *p++ = *num++; *p = '\0'; return(pbuf); } static void find_idle_and_ttywrite(WHERE *w) { struct stat sb; time_t touched; int error; (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_DEV, w->tty); error = stat(tbuf, &sb); if (error < 0 && errno == ENOENT) { /* * The terminal listed is not actually a terminal (i.e., * ":0"). This is a failure, so we'll skip printing * out the idle time, which is non-ideal but better * than a bogus warning and idle time. */ w->idletime = -1; return; } else if (error < 0) { warn("%s", tbuf); w->idletime = -1; return; } touched = sb.st_atime; if (touched < w->loginat) { /* tty untouched since before login */ touched = w->loginat; } w->idletime = now < touched ? 0 : now - touched; #define TALKABLE 0220 /* tty is writable if 220 mode */ w->writable = ((sb.st_mode & TALKABLE) == TALKABLE); } static void userinfo(PERSON *pn, struct passwd *pw) { char *p, *t; char *bp, name[1024]; struct stat sb; pn->realname = pn->office = pn->officephone = pn->homephone = NULL; pn->uid = pw->pw_uid; if ((pn->name = strdup(pw->pw_name)) == NULL) err(1, "strdup failed"); if ((pn->dir = strdup(pw->pw_dir)) == NULL) err(1, "strdup failed"); if ((pn->shell = strdup(pw->pw_shell)) == NULL) err(1, "strdup failed"); /* why do we skip asterisks!?!? */ (void)strncpy(bp = tbuf, pw->pw_gecos, sizeof(tbuf)); tbuf[sizeof(tbuf) - 1] = '\0'; if (*bp == '*') ++bp; /* ampersands get replaced by the login name */ if (!(p = strsep(&bp, ","))) return; for (t = name; t < &name[sizeof(name) - 1] && (*t = *p) != '\0'; ++p) { if (*t == '&') { (void)strncpy(t, pw->pw_name, sizeof(name) - (t - name)); name[sizeof(name) - 1] = '\0'; if (islower(*t)) *t = toupper(*t); while (t < &name[sizeof(name) - 1] && *++t) continue; } else { ++t; } } *t = '\0'; if ((pn->realname = strdup(name)) == NULL) err(1, "strdup failed"); pn->office = ((p = strsep(&bp, ",")) && *p) ? strdup(p) : NULL; pn->officephone = ((p = strsep(&bp, ",")) && *p) ? strdup(p) : NULL; pn->homephone = ((p = strsep(&bp, ",")) && *p) ? strdup(p) : NULL; (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pw->pw_name); pn->mailrecv = -1; /* -1 == not_valid */ if (stat(tbuf, &sb) < 0) { if (errno != ENOENT) { warn("%s", tbuf); return; } } else if (sb.st_size != 0) { pn->mailrecv = sb.st_mtime; pn->mailread = sb.st_atime; } } /* * Is this user hiding from finger? * If ~/.nofinger exists, return 1 (hide), else return 0 (nohide). * Nobody can hide from root. */ int hide(struct passwd *pw) { struct stat st; char buf[MAXPATHLEN]; if (invoker_root || !pw->pw_dir) return 0; snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, _PATH_NOFINGER); if (stat(buf, &st) == 0) return 1; return 0; } diff --git a/usr.bin/fstat/fstat.c b/usr.bin/fstat/fstat.c index c3374ecfffac..328e35e9138f 100644 --- a/usr.bin/fstat/fstat.c +++ b/usr.bin/fstat/fstat.c @@ -1,649 +1,648 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2009 Stanislav Sedov * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "functions.h" static int fsflg, /* show files on same filesystem as file(s) argument */ pflg, /* show files open by a particular pid */ sflg, /* show socket details */ uflg; /* show files open by a particular (effective) user */ static int checkfile; /* restrict to particular files or filesystems */ static int nflg; /* (numerical) display f.s. and rdev as dev_t */ static int mflg; /* include memory-mapped files */ static int vflg; /* be verbose */ typedef struct devs { struct devs *next; uint64_t fsid; uint64_t ino; const char *name; } DEVS; static DEVS *devs; static char *memf, *nlistf; static int getfname(const char *filename); static void dofiles(struct procstat *procstat, struct kinfo_proc *p); static void print_access_flags(int flags); static void print_file_info(struct procstat *procstat, struct filestat *fst, const char *uname, const char *cmd, int pid); static void print_pipe_info(struct procstat *procstat, struct filestat *fst); static void print_pts_info(struct procstat *procstat, struct filestat *fst); static void print_sem_info(struct procstat *procstat, struct filestat *fst); static void print_shm_info(struct procstat *procstat, struct filestat *fst); static void print_socket_info(struct procstat *procstat, struct filestat *fst); static void print_vnode_info(struct procstat *procstat, struct filestat *fst); static void usage(void) __dead2; int do_fstat(int argc, char **argv) { struct kinfo_proc *p; struct passwd *passwd; struct procstat *procstat; int arg, ch, what; int cnt, i; arg = 0; what = KERN_PROC_PROC; nlistf = memf = NULL; while ((ch = getopt(argc, argv, "fmnp:su:vN:M:")) != -1) switch((char)ch) { case 'f': fsflg = 1; break; case 'M': memf = optarg; break; case 'N': nlistf = optarg; break; case 'm': mflg = 1; break; case 'n': nflg = 1; break; case 'p': if (pflg++) usage(); if (!isdigit(*optarg)) { warnx("-p requires a process id"); usage(); } what = KERN_PROC_PID; arg = atoi(optarg); break; case 's': sflg = 1; break; case 'u': if (uflg++) usage(); if (!(passwd = getpwnam(optarg))) errx(1, "%s: unknown uid", optarg); what = KERN_PROC_UID; arg = passwd->pw_uid; break; case 'v': vflg = 1; break; case '?': default: usage(); } if (*(argv += optind)) { for (; *argv; ++argv) { if (getfname(*argv)) checkfile = 1; } if (!checkfile) /* file(s) specified, but none accessible */ exit(1); } if (fsflg && !checkfile) { /* -f with no files means use wd */ if (getfname(".") == 0) exit(1); checkfile = 1; } if (memf != NULL) procstat = procstat_open_kvm(nlistf, memf); else procstat = procstat_open_sysctl(); if (procstat == NULL) errx(1, "procstat_open()"); p = procstat_getprocs(procstat, what, arg, &cnt); if (p == NULL) errx(1, "procstat_getprocs()"); /* * Print header. */ if (nflg) printf("%s", "USER CMD PID FD DEV INUM MODE SZ|DV R/W"); else printf("%s", "USER CMD PID FD MOUNT INUM MODE SZ|DV R/W"); if (checkfile && fsflg == 0) printf(" NAME\n"); else putchar('\n'); /* * Go through the process list. */ for (i = 0; i < cnt; i++) { if (p[i].ki_stat == SZOMB) continue; dofiles(procstat, &p[i]); } procstat_freeprocs(procstat, p); procstat_close(procstat); return (0); } static void dofiles(struct procstat *procstat, struct kinfo_proc *kp) { const char *cmd; const char *uname; struct filestat *fst; struct filestat_list *head; int pid; uname = user_from_uid(kp->ki_uid, 0); pid = kp->ki_pid; cmd = kp->ki_comm; head = procstat_getfiles(procstat, kp, mflg); if (head == NULL) return; STAILQ_FOREACH(fst, head, next) print_file_info(procstat, fst, uname, cmd, pid); procstat_freefiles(procstat, head); } static void print_file_info(struct procstat *procstat, struct filestat *fst, const char *uname, const char *cmd, int pid) { struct vnstat vn; DEVS *d; const char *filename; int error, fsmatch = 0; char errbuf[_POSIX2_LINE_MAX]; filename = NULL; if (checkfile != 0) { if (fst->fs_type != PS_FST_TYPE_VNODE && fst->fs_type != PS_FST_TYPE_FIFO) return; error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); if (error != 0) return; for (d = devs; d != NULL; d = d->next) if (d->fsid == vn.vn_fsid) { fsmatch = 1; if (d->ino == vn.vn_fileid) { filename = d->name; break; } } if (fsmatch == 0 || (filename == NULL && fsflg == 0)) return; } /* * Print entry prefix. */ printf("%-8.8s %-10s %5d", uname, cmd, pid); if (fst->fs_uflags & PS_FST_UFLAG_TEXT) printf(" text"); else if (fst->fs_uflags & PS_FST_UFLAG_CDIR) printf(" wd"); else if (fst->fs_uflags & PS_FST_UFLAG_RDIR) printf(" root"); else if (fst->fs_uflags & PS_FST_UFLAG_TRACE) printf(" tr"); else if (fst->fs_uflags & PS_FST_UFLAG_MMAP) printf(" mmap"); else if (fst->fs_uflags & PS_FST_UFLAG_JAIL) printf(" jail"); else if (fst->fs_uflags & PS_FST_UFLAG_CTTY) printf(" ctty"); else printf(" %4d", fst->fs_fd); /* * Print type-specific data. */ switch (fst->fs_type) { case PS_FST_TYPE_FIFO: case PS_FST_TYPE_VNODE: print_vnode_info(procstat, fst); break; case PS_FST_TYPE_SOCKET: print_socket_info(procstat, fst); break; case PS_FST_TYPE_PIPE: print_pipe_info(procstat, fst); break; case PS_FST_TYPE_PTS: print_pts_info(procstat, fst); break; case PS_FST_TYPE_SHM: print_shm_info(procstat, fst); break; case PS_FST_TYPE_SEM: print_sem_info(procstat, fst); break; case PS_FST_TYPE_DEV: break; default: if (vflg) fprintf(stderr, "unknown file type %d for file %d of pid %d\n", fst->fs_type, fst->fs_fd, pid); } if (filename && !fsflg) printf(" %s", filename); putchar('\n'); } static char * addr_to_string(struct sockaddr_storage *ss, char *buffer, int buflen) { char buffer2[INET6_ADDRSTRLEN]; struct sockaddr_in6 *sin6; struct sockaddr_in *sin; struct sockaddr_un *sun; switch (ss->ss_family) { case AF_LOCAL: sun = (struct sockaddr_un *)ss; if (strlen(sun->sun_path) == 0) strlcpy(buffer, "-", buflen); else strlcpy(buffer, sun->sun_path, buflen); break; case AF_INET: sin = (struct sockaddr_in *)ss; if (sin->sin_addr.s_addr == INADDR_ANY) snprintf(buffer, buflen, "%s:%d", "*", ntohs(sin->sin_port)); else if (inet_ntop(AF_INET, &sin->sin_addr, buffer2, sizeof(buffer2)) != NULL) snprintf(buffer, buflen, "%s:%d", buffer2, ntohs(sin->sin_port)); break; case AF_INET6: sin6 = (struct sockaddr_in6 *)ss; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) snprintf(buffer, buflen, "%s.%d", "*", ntohs(sin6->sin6_port)); else if (inet_ntop(AF_INET6, &sin6->sin6_addr, buffer2, sizeof(buffer2)) != NULL) snprintf(buffer, buflen, "%s.%d", buffer2, ntohs(sin6->sin6_port)); else strlcpy(buffer, "-", buflen); break; default: strlcpy(buffer, "", buflen); break; } return buffer; } static void print_socket_info(struct procstat *procstat, struct filestat *fst) { static const char *stypename[] = { "unused", /* 0 */ "stream", /* 1 */ "dgram", /* 2 */ "raw", /* 3 */ "rdm", /* 4 */ "seqpak" /* 5 */ }; #define STYPEMAX 5 struct sockstat sock; struct protoent *pe; char errbuf[_POSIX2_LINE_MAX]; char src_addr[PATH_MAX], dst_addr[PATH_MAX]; struct sockaddr_un *sun; int error; static int isopen; error = procstat_get_socket_info(procstat, fst, &sock, errbuf); if (error != 0) { printf("* error"); return; } if (sock.type > STYPEMAX) printf("* %s ?%d", sock.dname, sock.type); else printf("* %s %s", sock.dname, stypename[sock.type]); /* * protocol specific formatting * * Try to find interesting things to print. For tcp, the interesting * thing is the address of the tcpcb, for udp and others, just the * inpcb (socket pcb). For unix domain, its the address of the socket * pcb and the address of the connected pcb (if connected). Otherwise * just print the protocol number and address of the socket itself. * The idea is not to duplicate netstat, but to make available enough * information for further analysis. */ switch (sock.dom_family) { case AF_INET: case AF_INET6: if (!isopen) setprotoent(++isopen); if ((pe = getprotobynumber(sock.proto)) != NULL) printf(" %s", pe->p_name); else printf(" %d", sock.proto); if (sock.proto == IPPROTO_TCP ) { if (sock.inp_ppcb != 0) printf(" %lx", (u_long)sock.inp_ppcb); } else if (sock.so_pcb != 0) printf(" %lx", (u_long)sock.so_pcb); if (!sflg) break; printf(" %s <-> %s", addr_to_string(&sock.sa_local, src_addr, sizeof(src_addr)), addr_to_string(&sock.sa_peer, dst_addr, sizeof(dst_addr))); break; case AF_UNIX: /* print address of pcb and connected pcb */ if (sock.so_pcb != 0) { printf(" %lx", (u_long)sock.so_pcb); if (sock.unp_conn) { char shoconn[4], *cp; cp = shoconn; if (!(sock.so_rcv_sb_state & SBS_CANTRCVMORE)) *cp++ = '<'; *cp++ = '-'; if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE)) *cp++ = '>'; *cp = '\0'; printf(" %s %lx", shoconn, (u_long)sock.unp_conn); } } if (!sflg) break; sun = (struct sockaddr_un *)&sock.sa_local; /* * While generally we like to print two addresses, * local and peer, for sockets, it turns out to be * more useful to print the first non-null address for * local sockets, as typically they aren't bound and * connected, and the path strings can get long. */ if (sun->sun_path[0] != 0) addr_to_string(&sock.sa_local, src_addr, sizeof(src_addr)); else addr_to_string(&sock.sa_peer, src_addr, sizeof(src_addr)); printf(" %s", src_addr); break; default: /* print protocol number and socket address */ printf(" %d %lx", sock.proto, (u_long)sock.so_addr); } } static void print_pipe_info(struct procstat *procstat, struct filestat *fst) { struct pipestat ps; char errbuf[_POSIX2_LINE_MAX]; int error; error = procstat_get_pipe_info(procstat, fst, &ps, errbuf); if (error != 0) { printf("* error"); return; } printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer); printf(" %6zd", ps.buffer_cnt); print_access_flags(fst->fs_fflags); } static void print_pts_info(struct procstat *procstat, struct filestat *fst) { struct ptsstat pts; char errbuf[_POSIX2_LINE_MAX]; int error; error = procstat_get_pts_info(procstat, fst, &pts, errbuf); if (error != 0) { printf("* error"); return; } printf("* pseudo-terminal master "); if (nflg || !*pts.devname) { printf("%#10jx", (uintmax_t)pts.dev); } else { printf("%10s", pts.devname); } print_access_flags(fst->fs_fflags); } static void print_sem_info(struct procstat *procstat, struct filestat *fst) { struct semstat sem; char errbuf[_POSIX2_LINE_MAX]; char mode[15]; int error; error = procstat_get_sem_info(procstat, fst, &sem, errbuf); if (error != 0) { printf("* error"); return; } if (nflg) { printf(" "); (void)snprintf(mode, sizeof(mode), "%o", sem.mode); } else { printf(" %-15s", fst->fs_path != NULL ? fst->fs_path : "-"); strmode(sem.mode, mode); } printf(" %10s %6u", mode, sem.value); print_access_flags(fst->fs_fflags); } static void print_shm_info(struct procstat *procstat, struct filestat *fst) { struct shmstat shm; char errbuf[_POSIX2_LINE_MAX]; char mode[15]; int error; error = procstat_get_shm_info(procstat, fst, &shm, errbuf); if (error != 0) { printf("* error"); return; } if (nflg) { printf(" "); (void)snprintf(mode, sizeof(mode), "%o", shm.mode); } else { printf(" %-15s", fst->fs_path != NULL ? fst->fs_path : "-"); strmode(shm.mode, mode); } printf(" %10s %6ju", mode, shm.size); print_access_flags(fst->fs_fflags); } static void print_vnode_info(struct procstat *procstat, struct filestat *fst) { struct vnstat vn; char errbuf[_POSIX2_LINE_MAX]; char mode[15]; const char *badtype; int error; badtype = NULL; error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); if (error != 0) badtype = errbuf; else if (vn.vn_type == PS_FST_VTYPE_VBAD) badtype = "bad"; else if (vn.vn_type == PS_FST_VTYPE_VNON) badtype = "none"; if (badtype != NULL) { printf(" - - %10s -", badtype); return; } if (nflg) printf(" %#5jx", (uintmax_t)vn.vn_fsid); else if (vn.vn_mntdir != NULL) (void)printf(" %-8s", vn.vn_mntdir); /* * Print access mode. */ if (nflg) (void)snprintf(mode, sizeof(mode), "%o", vn.vn_mode); else { strmode(vn.vn_mode, mode); } (void)printf(" %6jd %10s", (intmax_t)vn.vn_fileid, mode); if (vn.vn_type == PS_FST_VTYPE_VBLK || vn.vn_type == PS_FST_VTYPE_VCHR) { if (nflg || !*vn.vn_devname) printf(" %#6jx", (uintmax_t)vn.vn_dev); else { printf(" %6s", vn.vn_devname); } } else printf(" %6ju", (uintmax_t)vn.vn_size); print_access_flags(fst->fs_fflags); } static void print_access_flags(int flags) { char rw[3]; rw[0] = '\0'; if (flags & PS_FST_FFLAG_READ) strcat(rw, "r"); if (flags & PS_FST_FFLAG_WRITE) strcat(rw, "w"); printf(" %2s", rw); } int getfname(const char *filename) { struct stat statbuf; DEVS *cur; if (stat(filename, &statbuf)) { warn("%s", filename); return (0); } if ((cur = malloc(sizeof(DEVS))) == NULL) err(1, NULL); cur->next = devs; devs = cur; cur->ino = statbuf.st_ino; cur->fsid = statbuf.st_dev; cur->name = filename; return (1); } static void usage(void) { (void)fprintf(stderr, "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] [file ...]\n"); exit(1); } diff --git a/usr.bin/fsync/fsync.c b/usr.bin/fsync/fsync.c index 16d5a176da93..9968bc8614a5 100644 --- a/usr.bin/fsync/fsync.c +++ b/usr.bin/fsync/fsync.c @@ -1,80 +1,77 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2000 Paul Saab * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include #include #include #include #include #include static void usage(void); int main(int argc, char *argv[]) { int fd; int i; int rval; if (argc < 2) { usage(); /* NOTREACHED */ } rval = EX_OK; for (i = 1; i < argc; ++i) { if ((fd = open(argv[i], O_RDONLY)) == -1) { warn("open %s", argv[i]); if (rval == EX_OK) rval = EX_NOINPUT; continue; } if (fsync(fd) == -1) { warn("fsync %s", argv[i]); if (rval == EX_OK) rval = EX_OSERR; } close(fd); } exit(rval); /* NOTREACHED */ } static void usage(void) { fprintf(stderr, "usage: fsync file ...\n"); exit(EX_USAGE); /* NOTREACHED */ } diff --git a/usr.bin/getaddrinfo/getaddrinfo.c b/usr.bin/getaddrinfo/getaddrinfo.c index f01ccd7b2e10..da84259a3c2e 100644 --- a/usr.bin/getaddrinfo/getaddrinfo.c +++ b/usr.bin/getaddrinfo/getaddrinfo.c @@ -1,329 +1,328 @@ /* $NetBSD: getaddrinfo.c,v 1.4 2014/04/22 02:23:03 ginsbach Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Taylor R. Campbell. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tables.h" static void usage(void) __dead; static void printaddrinfo(struct addrinfo *); static bool parse_af(const char *, int *); static bool parse_protocol(const char *, int *); static bool parse_socktype(const char *, int *); static bool parse_numeric_tabular(const char *, int *, const char *const *, size_t); int main(int argc, char **argv) { static const struct addrinfo zero_addrinfo; struct addrinfo hints = zero_addrinfo; struct addrinfo *addrinfo; const char *hostname = NULL, *service = NULL; int ch; int error; setprogname(argv[0]); hints.ai_family = AF_UNSPEC; hints.ai_socktype = 0; hints.ai_protocol = 0; hints.ai_flags = 0; while ((ch = getopt(argc, argv, "cf:nNp:Ps:t:")) != -1) { switch (ch) { case 'c': hints.ai_flags |= AI_CANONNAME; break; case 'f': if (!parse_af(optarg, &hints.ai_family)) { warnx("invalid address family: %s", optarg); usage(); } break; case 'n': hints.ai_flags |= AI_NUMERICHOST; break; case 'N': hints.ai_flags |= AI_NUMERICSERV; break; case 's': service = optarg; break; case 'p': if (!parse_protocol(optarg, &hints.ai_protocol)) { warnx("invalid protocol: %s", optarg); usage(); } break; case 'P': hints.ai_flags |= AI_PASSIVE; break; case 't': if (!parse_socktype(optarg, &hints.ai_socktype)) { warnx("invalid socket type: %s", optarg); usage(); } break; case '?': default: usage(); } } argc -= optind; argv += optind; if (!((argc == 1) || ((argc == 0) && (hints.ai_flags & AI_PASSIVE)))) usage(); if (argc == 1) hostname = argv[0]; if (service != NULL) { char *p; if ((p = strchr(service, '/')) != NULL) { if (hints.ai_protocol != 0) { warnx("protocol already specified"); usage(); } *p = '\0'; p++; if (!parse_protocol(p, &hints.ai_protocol)) { warnx("invalid protocol: %s", p); usage(); } } } error = getaddrinfo(hostname, service, &hints, &addrinfo); if (error) errx(1, "%s", gai_strerror(error)); if ((hints.ai_flags & AI_CANONNAME) && (addrinfo != NULL)) { if (printf("canonname %s\n", addrinfo->ai_canonname) < 0) err(1, "printf"); } printaddrinfo(addrinfo); freeaddrinfo(addrinfo); return 0; } static void __dead usage(void) { (void)fprintf(stderr, "Usage: %s", getprogname()); (void)fprintf(stderr, " [-f ] [-p ] [-t ] [-s ]\n"); (void)fprintf(stderr, " [-cnNP] []\n"); exit(1); } static bool parse_af(const char *string, int *afp) { return parse_numeric_tabular(string, afp, address_families, __arraycount(address_families)); } static bool parse_protocol(const char *string, int *protop) { struct protoent *protoent; char *end; long value; errno = 0; value = strtol(string, &end, 0); if ((string[0] == '\0') || (*end != '\0')) goto numeric_failed; if ((errno == ERANGE) && ((value == LONG_MAX) || (value == LONG_MIN))) goto numeric_failed; if ((value > INT_MAX) || (value < INT_MIN)) goto numeric_failed; *protop = value; return true; numeric_failed: protoent = getprotobyname(string); if (protoent == NULL) goto protoent_failed; *protop = protoent->p_proto; return true; protoent_failed: return false; } static bool parse_socktype(const char *string, int *typep) { return parse_numeric_tabular(string, typep, socket_types, __arraycount(socket_types)); } static bool parse_numeric_tabular(const char *string, int *valuep, const char *const *table, size_t n) { char *end; long value; size_t i; assert((uintmax_t)n <= (uintmax_t)INT_MAX); errno = 0; value = strtol(string, &end, 0); if ((string[0] == '\0') || (*end != '\0')) goto numeric_failed; if ((errno == ERANGE) && ((value == LONG_MAX) || (value == LONG_MIN))) goto numeric_failed; if ((value > INT_MAX) || (value < INT_MIN)) goto numeric_failed; *valuep = value; return true; numeric_failed: for (i = 0; i < n; i++) if ((table[i] != NULL) && (strcmp(string, table[i]) == 0)) break; if (i == n) goto table_failed; *valuep = i; return true; table_failed: return false; } static void printaddrinfo(struct addrinfo *addrinfo) { struct addrinfo *ai; char buf[1024]; int n; struct protoent *protoent; for (ai = addrinfo; ai != NULL; ai = ai->ai_next) { /* Print the socket type. */ if ((ai->ai_socktype >= 0) && ((size_t)ai->ai_socktype < __arraycount(socket_types)) && (socket_types[ai->ai_socktype] != NULL)) n = printf("%s", socket_types[ai->ai_socktype]); else n = printf("%d", ai->ai_socktype); if (n < 0) err(1, "printf"); /* Print the address family. */ if ((ai->ai_family >= 0) && ((size_t)ai->ai_family < __arraycount(address_families)) && (address_families[ai->ai_family] != NULL)) n = printf(" %s", address_families[ai->ai_family]); else n = printf(" %d", ai->ai_family); if (n < 0) err(1, "printf"); /* Print the protocol number. */ protoent = getprotobynumber(ai->ai_protocol); if (protoent == NULL) n = printf(" %d", ai->ai_protocol); else n = printf(" %s", protoent->p_name); if (n < 0) err(1, "printf"); /* Format the sockaddr. */ switch (ai->ai_family) { case AF_INET: case AF_INET6: n = sockaddr_snprintf(buf, sizeof(buf), " %a %p", ai->ai_addr); break; default: n = sockaddr_snprintf(buf, sizeof(buf), "%a %p %I %F %R %S", ai->ai_addr); } /* * Check for sockaddr_snprintf failure. * * XXX sockaddr_snprintf's error reporting is botched * -- man page says it sets errno, but if getnameinfo * fails, errno is not where it reports the error... */ if (n < 0) { warnx("sockaddr_snprintf failed"); continue; } if (sizeof(buf) <= (size_t)n) warnx("truncated sockaddr_snprintf output"); /* Print the formatted sockaddr. */ if (printf("%s\n", buf) < 0) err(1, "printf"); } } diff --git a/usr.bin/getconf/getconf.c b/usr.bin/getconf/getconf.c index a4434c31fd36..ed69d5f0454b 100644 --- a/usr.bin/getconf/getconf.c +++ b/usr.bin/getconf/getconf.c @@ -1,286 +1,285 @@ /* * Copyright 2000 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include "getconf.h" static void do_allsys(void); static void do_allpath(const char *path); static void do_confstr(const char *name, int key); static void do_sysconf(const char *name, int key); static void do_pathconf(const char *name, int key, const char *path); static void usage(void) { fprintf(stderr, "usage: getconf -a [pathname]\n" " getconf [-v prog_env] system_var\n" " getconf [-v prog_env] path_var pathname\n"); exit(EX_USAGE); } int main(int argc, char **argv) { bool aflag; int c, key, valid; const char *name, *vflag, *alt_path; intmax_t limitval; uintmax_t ulimitval; aflag = false; vflag = NULL; while ((c = getopt(argc, argv, "av:")) != -1) { switch (c) { case 'a': aflag = true; break; case 'v': vflag = optarg; break; default: usage(); } } if (aflag) { if (vflag != NULL) usage(); if (argv[optind] == NULL) do_allsys(); else do_allpath(argv[optind]); return (0); } if ((name = argv[optind]) == NULL) usage(); if (vflag != NULL) { if ((valid = find_progenv(vflag, &alt_path)) == 0) errx(EX_USAGE, "invalid programming environment %s", vflag); if (valid > 0 && alt_path != NULL) { if (argv[optind + 1] == NULL) execl(alt_path, "getconf", argv[optind], (char *)NULL); else execl(alt_path, "getconf", argv[optind], argv[optind + 1], (char *)NULL); err(EX_OSERR, "execl: %s", alt_path); } if (valid < 0) errx(EX_UNAVAILABLE, "environment %s is not available", vflag); } if (argv[optind + 1] == NULL) { /* confstr or sysconf */ if ((valid = find_unsigned_limit(name, &ulimitval)) != 0) { if (valid > 0) printf("%" PRIuMAX "\n", ulimitval); else printf("undefined\n"); return 0; } if ((valid = find_limit(name, &limitval)) != 0) { if (valid > 0) printf("%" PRIdMAX "\n", limitval); else printf("undefined\n"); return 0; } if ((valid = find_confstr(name, &key)) != 0) { if (valid > 0) do_confstr(name, key); else printf("undefined\n"); } else { valid = find_sysconf(name, &key); if (valid > 0) { do_sysconf(name, key); } else if (valid < 0) { printf("undefined\n"); } else errx(EX_USAGE, "no such configuration parameter `%s'", name); } } else { valid = find_pathconf(name, &key); if (valid != 0) { if (valid > 0) do_pathconf(name, key, argv[optind + 1]); else printf("undefined\n"); } else errx(EX_USAGE, "no such path configuration parameter `%s'", name); } return 0; } static void do_onestr(const char *name, int key) { size_t len; errno = 0; len = confstr(key, 0, 0); if (len == 0 && errno != 0) { warn("confstr: %s", name); return; } printf("%s: ", name); if (len == 0) printf("undefined\n"); else { char buf[len + 1]; confstr(key, buf, len); printf("%s\n", buf); } } static void do_onesys(const char *name, int key) { long value; errno = 0; value = sysconf(key); if (value == -1 && errno != 0) { warn("sysconf: %s", name); return; } printf("%s: ", name); if (value == -1) printf("undefined\n"); else printf("%ld\n", value); } static void do_allsys(void) { foreach_confstr(do_onestr); foreach_sysconf(do_onesys); } static void do_onepath(const char *name, int key, const char *path) { long value; errno = 0; value = pathconf(path, key); if (value == -1 && errno != EINVAL && errno != 0) warn("pathconf: %s", name); printf("%s: ", name); if (value == -1) printf("undefined\n"); else printf("%ld\n", value); } static void do_allpath(const char *path) { foreach_pathconf(do_onepath, path); } static void do_confstr(const char *name, int key) { size_t len; int savederr; savederr = errno; errno = 0; len = confstr(key, 0, 0); if (len == 0) { if (errno) err(EX_OSERR, "confstr: %s", name); else printf("undefined\n"); } else { char buf[len + 1]; confstr(key, buf, len); printf("%s\n", buf); } errno = savederr; } static void do_sysconf(const char *name, int key) { long value; errno = 0; value = sysconf(key); if (value == -1 && errno != 0) err(EX_OSERR, "sysconf: %s", name); else if (value == -1) printf("undefined\n"); else printf("%ld\n", value); } static void do_pathconf(const char *name, int key, const char *path) { long value; errno = 0; value = pathconf(path, key); if (value == -1 && errno != 0) err(EX_OSERR, "pathconf: %s", name); else if (value == -1) printf("undefined\n"); else printf("%ld\n", value); } diff --git a/usr.bin/gprof/arcs.c b/usr.bin/gprof/arcs.c index fe032e68a516..5e8ba06402a3 100644 --- a/usr.bin/gprof/arcs.c +++ b/usr.bin/gprof/arcs.c @@ -1,950 +1,947 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include #include #include "gprof.h" #ifdef DEBUG int visited; int viable; int newcycle; int oldcycle; #endif /* DEBUG */ int topcmp(const void *, const void *); /* * add (or just increment) an arc */ void addarc(nltype *parentp, nltype *childp, long count) { arctype *arcp; # ifdef DEBUG if ( debug & TALLYDEBUG ) { printf( "[addarc] %ld arcs from %s to %s\n" , count , parentp -> name , childp -> name ); } # endif /* DEBUG */ arcp = arclookup( parentp , childp ); if ( arcp != 0 ) { /* * a hit: just increment the count. */ # ifdef DEBUG if ( debug & TALLYDEBUG ) { printf( "[tally] hit %ld += %ld\n" , arcp -> arc_count , count ); } # endif /* DEBUG */ arcp -> arc_count += count; return; } arcp = (arctype *)calloc( 1 , sizeof *arcp ); if (arcp == NULL) errx( 1 , "malloc failed" ); arcp -> arc_parentp = parentp; arcp -> arc_childp = childp; arcp -> arc_count = count; /* * prepend this child to the children of this parent */ arcp -> arc_childlist = parentp -> children; parentp -> children = arcp; /* * prepend this parent to the parents of this child */ arcp -> arc_parentlist = childp -> parents; childp -> parents = arcp; } /* * the code below topologically sorts the graph (collapsing cycles), * and propagates time bottom up and flags top down. */ /* * the topologically sorted name list pointers */ nltype **topsortnlp; int topcmp(const void *v1, const void *v2) { const nltype **npp1 = (const nltype **)v1; const nltype **npp2 = (const nltype **)v2; return (*npp1) -> toporder - (*npp2) -> toporder; } nltype ** doarcs(void) { nltype *parentp, **timesortnlp; arctype *arcp; long index; long pass; /* * initialize various things: * zero out child times. * count self-recursive calls. * indicate that nothing is on cycles. */ for ( parentp = nl ; parentp < npe ; parentp++ ) { parentp -> childtime = 0.0; arcp = arclookup( parentp , parentp ); if ( arcp != 0 ) { parentp -> ncall -= arcp -> arc_count; parentp -> selfcalls = arcp -> arc_count; } else { parentp -> selfcalls = 0; } parentp -> npropcall = parentp -> ncall; parentp -> propfraction = 0.0; parentp -> propself = 0.0; parentp -> propchild = 0.0; parentp -> printflag = FALSE; parentp -> toporder = DFN_NAN; parentp -> cycleno = 0; parentp -> cyclehead = parentp; parentp -> cnext = 0; } for ( pass = 1 ; ; pass++ ) { /* * topologically order things * if any node is unnumbered, * number it and any of its descendents. */ for ( dfn_init() , parentp = nl ; parentp < npe ; parentp++ ) { if ( parentp -> toporder == DFN_NAN ) { dfn( parentp ); } } /* * link together nodes on the same cycle */ cyclelink(); /* * if no cycles to break up, proceed */ if ( ! Cflag ) break; /* * analyze cycles to determine breakup */ # ifdef DEBUG if ( debug & BREAKCYCLE ) { printf("[doarcs] pass %ld, cycle(s) %d\n" , pass , ncycle ); } # endif /* DEBUG */ if ( pass == 1 ) { printf( "\n\n%s %s\n%s %d:\n" , "The following arcs were deleted" , "from the propagation calculation" , "to reduce the maximum cycle size to", cyclethreshold ); } if ( cycleanalyze() ) break; free ( cyclenl ); ncycle = 0; for ( parentp = nl ; parentp < npe ; parentp++ ) { parentp -> toporder = DFN_NAN; parentp -> cycleno = 0; parentp -> cyclehead = parentp; parentp -> cnext = 0; } } if ( pass > 1 ) { printf( "\f\n" ); } else { printf( "\tNone\n\n" ); } /* * Sort the symbol table in reverse topological order */ topsortnlp = (nltype **) calloc( nname , sizeof(nltype *) ); if ( topsortnlp == (nltype **) 0 ) errx( 1 , "[doarcs] ran out of memory for topo sorting" ); for ( index = 0 ; index < nname ; index += 1 ) { topsortnlp[ index ] = &nl[ index ]; } qsort( topsortnlp , nname , sizeof(nltype *) , topcmp ); # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "[doarcs] topological sort listing\n" ); for ( index = 0 ; index < nname ; index += 1 ) { printf( "[doarcs] " ); printf( "%d:" , topsortnlp[ index ] -> toporder ); printname( topsortnlp[ index ] ); printf( "\n" ); } } # endif /* DEBUG */ /* * starting from the topological top, * propagate print flags to children. * also, calculate propagation fractions. * this happens before time propagation * since time propagation uses the fractions. */ doflags(); /* * starting from the topological bottom, * propagate children times up to parents. */ dotime(); /* * Now, sort by propself + propchild. * sorting both the regular function names * and cycle headers. */ timesortnlp = (nltype **) calloc( nname + ncycle , sizeof(nltype *) ); if ( timesortnlp == (nltype **) 0 ) errx( 1 , "ran out of memory for sorting" ); for ( index = 0 ; index < nname ; index++ ) { timesortnlp[index] = &nl[index]; } for ( index = 1 ; index <= ncycle ; index++ ) { timesortnlp[nname+index-1] = &cyclenl[index]; } qsort( timesortnlp , nname + ncycle , sizeof(nltype *) , totalcmp ); for ( index = 0 ; index < nname + ncycle ; index++ ) { timesortnlp[ index ] -> index = index + 1; } return( timesortnlp ); } void dotime(void) { int index; cycletime(); for ( index = 0 ; index < nname ; index += 1 ) { timepropagate( topsortnlp[ index ] ); } } void timepropagate(nltype *parentp) { arctype *arcp; nltype *childp; double share; double propshare; if ( parentp -> propfraction == 0.0 ) { return; } /* * gather time from children of this parent. */ for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) { childp = arcp -> arc_childp; if ( arcp -> arc_flags & DEADARC ) { continue; } if ( arcp -> arc_count == 0 ) { continue; } if ( childp == parentp ) { continue; } if ( childp -> propfraction == 0.0 ) { continue; } if ( childp -> cyclehead != childp ) { if ( parentp -> cycleno == childp -> cycleno ) { continue; } if ( parentp -> toporder <= childp -> toporder ) { fprintf( stderr , "[propagate] toporder botches\n" ); } childp = childp -> cyclehead; } else { if ( parentp -> toporder <= childp -> toporder ) { fprintf( stderr , "[propagate] toporder botches\n" ); continue; } } if ( childp -> npropcall == 0 ) { continue; } /* * distribute time for this arc */ arcp -> arc_time = childp -> time * ( ( (double) arcp -> arc_count ) / ( (double) childp -> npropcall ) ); arcp -> arc_childtime = childp -> childtime * ( ( (double) arcp -> arc_count ) / ( (double) childp -> npropcall ) ); share = arcp -> arc_time + arcp -> arc_childtime; parentp -> childtime += share; /* * ( 1 - propfraction ) gets lost along the way */ propshare = parentp -> propfraction * share; /* * fix things for printing */ parentp -> propchild += propshare; arcp -> arc_time *= parentp -> propfraction; arcp -> arc_childtime *= parentp -> propfraction; /* * add this share to the parent's cycle header, if any. */ if ( parentp -> cyclehead != parentp ) { parentp -> cyclehead -> childtime += share; parentp -> cyclehead -> propchild += propshare; } # ifdef DEBUG if ( debug & PROPDEBUG ) { printf( "[dotime] child \t" ); printname( childp ); printf( " with %f %f %ld/%ld\n" , childp -> time , childp -> childtime , arcp -> arc_count , childp -> npropcall ); printf( "[dotime] parent\t" ); printname( parentp ); printf( "\n[dotime] share %f\n" , share ); } # endif /* DEBUG */ } } void cyclelink(void) { register nltype *nlp; register nltype *cyclenlp; int cycle; nltype *memberp; arctype *arcp; /* * Count the number of cycles, and initialize the cycle lists */ ncycle = 0; for ( nlp = nl ; nlp < npe ; nlp++ ) { /* * this is how you find unattached cycles */ if ( nlp -> cyclehead == nlp && nlp -> cnext != 0 ) { ncycle += 1; } } /* * cyclenl is indexed by cycle number: * i.e. it is origin 1, not origin 0. */ cyclenl = (nltype *) calloc( ncycle + 1 , sizeof( nltype ) ); if ( cyclenl == NULL ) errx( 1 , "no room for %zu bytes of cycle headers" , ( ncycle + 1 ) * sizeof( nltype ) ); /* * now link cycles to true cycleheads, * number them, accumulate the data for the cycle */ cycle = 0; for ( nlp = nl ; nlp < npe ; nlp++ ) { if ( !( nlp -> cyclehead == nlp && nlp -> cnext != 0 ) ) { continue; } cycle += 1; cyclenlp = &cyclenl[cycle]; cyclenlp -> name = 0; /* the name */ cyclenlp -> value = 0; /* the pc entry point */ cyclenlp -> time = 0.0; /* ticks in this routine */ cyclenlp -> childtime = 0.0; /* cumulative ticks in children */ cyclenlp -> ncall = 0; /* how many times called */ cyclenlp -> selfcalls = 0; /* how many calls to self */ cyclenlp -> propfraction = 0.0; /* what % of time propagates */ cyclenlp -> propself = 0.0; /* how much self time propagates */ cyclenlp -> propchild = 0.0; /* how much child time propagates */ cyclenlp -> printflag = TRUE; /* should this be printed? */ cyclenlp -> index = 0; /* index in the graph list */ cyclenlp -> toporder = DFN_NAN; /* graph call chain top-sort order */ cyclenlp -> cycleno = cycle; /* internal number of cycle on */ cyclenlp -> cyclehead = cyclenlp; /* pointer to head of cycle */ cyclenlp -> cnext = nlp; /* pointer to next member of cycle */ cyclenlp -> parents = 0; /* list of caller arcs */ cyclenlp -> children = 0; /* list of callee arcs */ # ifdef DEBUG if ( debug & CYCLEDEBUG ) { printf( "[cyclelink] " ); printname( nlp ); printf( " is the head of cycle %d\n" , cycle ); } # endif /* DEBUG */ /* * link members to cycle header */ for ( memberp = nlp ; memberp ; memberp = memberp -> cnext ) { memberp -> cycleno = cycle; memberp -> cyclehead = cyclenlp; } /* * count calls from outside the cycle * and those among cycle members */ for ( memberp = nlp ; memberp ; memberp = memberp -> cnext ) { for ( arcp=memberp->parents ; arcp ; arcp=arcp->arc_parentlist ) { if ( arcp -> arc_parentp == memberp ) { continue; } if ( arcp -> arc_parentp -> cycleno == cycle ) { cyclenlp -> selfcalls += arcp -> arc_count; } else { cyclenlp -> npropcall += arcp -> arc_count; } } } } } /* * analyze cycles to determine breakup */ bool cycleanalyze(void) { arctype **cyclestack; arctype **stkp; arctype **arcpp; arctype **endlist; arctype *arcp; nltype *nlp; cltype *clp; bool ret; bool done; int size; int cycleno; /* * calculate the size of the cycle, and find nodes that * exit the cycle as they are desirable targets to cut * some of their parents */ for ( done = TRUE , cycleno = 1 ; cycleno <= ncycle ; cycleno++ ) { size = 0; for (nlp = cyclenl[ cycleno ] . cnext; nlp; nlp = nlp -> cnext) { size += 1; nlp -> parentcnt = 0; nlp -> flags &= ~HASCYCLEXIT; for ( arcp = nlp -> parents; arcp; arcp = arcp -> arc_parentlist ) { nlp -> parentcnt += 1; if ( arcp -> arc_parentp -> cycleno != cycleno ) nlp -> flags |= HASCYCLEXIT; } } if ( size <= cyclethreshold ) continue; done = FALSE; cyclestack = (arctype **) calloc( size + 1 , sizeof( arctype *) ); if ( cyclestack == NULL ) errx( 1, "no room for %zu bytes of cycle stack" , ( size + 1 ) * sizeof( arctype * ) ); # ifdef DEBUG if ( debug & BREAKCYCLE ) { printf( "[cycleanalyze] starting cycle %d of %d, size %d\n" , cycleno , ncycle , size ); } # endif /* DEBUG */ for ( nlp = cyclenl[ cycleno ] . cnext ; nlp ; nlp = nlp -> cnext ) { stkp = &cyclestack[0]; nlp -> flags |= CYCLEHEAD; ret = descend ( nlp , cyclestack , stkp ); nlp -> flags &= ~CYCLEHEAD; if ( ret == FALSE ) break; } free( cyclestack ); if ( cyclecnt > 0 ) { compresslist(); for ( clp = cyclehead ; clp ; ) { endlist = &clp -> list[ clp -> size ]; for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ ) (*arcpp) -> arc_cyclecnt--; cyclecnt--; clp = clp -> next; free( clp ); } cyclehead = 0; } } # ifdef DEBUG if ( debug & BREAKCYCLE ) { printf("%s visited %d, viable %d, newcycle %d, oldcycle %d\n", "[doarcs]" , visited , viable , newcycle , oldcycle); } # endif /* DEBUG */ return( done ); } bool descend(nltype *node, arctype **stkstart, arctype **stkp) { arctype *arcp; bool ret; for ( arcp = node -> children ; arcp ; arcp = arcp -> arc_childlist ) { # ifdef DEBUG visited++; # endif /* DEBUG */ if ( arcp -> arc_childp -> cycleno != node -> cycleno || ( arcp -> arc_childp -> flags & VISITED ) || ( arcp -> arc_flags & DEADARC ) ) continue; # ifdef DEBUG viable++; # endif /* DEBUG */ *stkp = arcp; if ( arcp -> arc_childp -> flags & CYCLEHEAD ) { if ( addcycle( stkstart , stkp ) == FALSE ) return( FALSE ); continue; } arcp -> arc_childp -> flags |= VISITED; ret = descend( arcp -> arc_childp , stkstart , stkp + 1 ); arcp -> arc_childp -> flags &= ~VISITED; if ( ret == FALSE ) return( FALSE ); } return( TRUE ); } bool addcycle(arctype **stkstart, arctype **stkend) { arctype **arcpp; arctype **stkloc; arctype **stkp; arctype **endlist; arctype *minarc; arctype *arcp; cltype *clp; int size; size = stkend - stkstart + 1; if ( size <= 1 ) return( TRUE ); for ( arcpp = stkstart , minarc = *arcpp ; arcpp <= stkend ; arcpp++ ) { if ( *arcpp > minarc ) continue; minarc = *arcpp; stkloc = arcpp; } for ( clp = cyclehead ; clp ; clp = clp -> next ) { if ( clp -> size != size ) continue; stkp = stkloc; endlist = &clp -> list[ size ]; for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ ) { if ( *stkp++ != *arcpp ) break; if ( stkp > stkend ) stkp = stkstart; } if ( arcpp == endlist ) { # ifdef DEBUG oldcycle++; # endif /* DEBUG */ return( TRUE ); } } clp = (cltype *) calloc( 1 , sizeof ( cltype ) + ( size - 1 ) * sizeof( arctype * ) ); if ( clp == NULL ) { warnx( "no room for %zu bytes of subcycle storage" , sizeof ( cltype ) + ( size - 1 ) * sizeof( arctype * ) ); return( FALSE ); } stkp = stkloc; endlist = &clp -> list[ size ]; for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ ) { arcp = *arcpp = *stkp++; if ( stkp > stkend ) stkp = stkstart; arcp -> arc_cyclecnt++; if ( ( arcp -> arc_flags & ONLIST ) == 0 ) { arcp -> arc_flags |= ONLIST; arcp -> arc_next = archead; archead = arcp; } } clp -> size = size; clp -> next = cyclehead; cyclehead = clp; # ifdef DEBUG newcycle++; if ( debug & SUBCYCLELIST ) { printsubcycle( clp ); } # endif /* DEBUG */ cyclecnt++; if ( cyclecnt >= CYCLEMAX ) return( FALSE ); return( TRUE ); } void compresslist(void) { cltype *clp; cltype **prev; arctype **arcpp; arctype **endlist; arctype *arcp; arctype *maxarcp; arctype *maxexitarcp; arctype *maxwithparentarcp; arctype *maxnoparentarcp; int maxexitcnt; int maxwithparentcnt; int maxnoparentcnt; # ifdef DEBUG const char *type; # endif /* DEBUG */ maxexitcnt = 0; maxwithparentcnt = 0; maxnoparentcnt = 0; for ( endlist = &archead , arcp = archead ; arcp ; ) { if ( arcp -> arc_cyclecnt == 0 ) { arcp -> arc_flags &= ~ONLIST; *endlist = arcp -> arc_next; arcp -> arc_next = 0; arcp = *endlist; continue; } if ( arcp -> arc_childp -> flags & HASCYCLEXIT ) { if ( arcp -> arc_cyclecnt > maxexitcnt || ( arcp -> arc_cyclecnt == maxexitcnt && arcp -> arc_cyclecnt < maxexitarcp -> arc_count ) ) { maxexitcnt = arcp -> arc_cyclecnt; maxexitarcp = arcp; } } else if ( arcp -> arc_childp -> parentcnt > 1 ) { if ( arcp -> arc_cyclecnt > maxwithparentcnt || ( arcp -> arc_cyclecnt == maxwithparentcnt && arcp -> arc_cyclecnt < maxwithparentarcp -> arc_count ) ) { maxwithparentcnt = arcp -> arc_cyclecnt; maxwithparentarcp = arcp; } } else { if ( arcp -> arc_cyclecnt > maxnoparentcnt || ( arcp -> arc_cyclecnt == maxnoparentcnt && arcp -> arc_cyclecnt < maxnoparentarcp -> arc_count ) ) { maxnoparentcnt = arcp -> arc_cyclecnt; maxnoparentarcp = arcp; } } endlist = &arcp -> arc_next; arcp = arcp -> arc_next; } if ( maxexitcnt > 0 ) { /* * first choice is edge leading to node with out-of-cycle parent */ maxarcp = maxexitarcp; # ifdef DEBUG type = "exit"; # endif /* DEBUG */ } else if ( maxwithparentcnt > 0 ) { /* * second choice is edge leading to node with at least one * other in-cycle parent */ maxarcp = maxwithparentarcp; # ifdef DEBUG type = "internal"; # endif /* DEBUG */ } else { /* * last choice is edge leading to node with only this arc as * a parent (as it will now be orphaned) */ maxarcp = maxnoparentarcp; # ifdef DEBUG type = "orphan"; # endif /* DEBUG */ } maxarcp -> arc_flags |= DEADARC; maxarcp -> arc_childp -> parentcnt -= 1; maxarcp -> arc_childp -> npropcall -= maxarcp -> arc_count; # ifdef DEBUG if ( debug & BREAKCYCLE ) { printf( "%s delete %s arc: %s (%ld) -> %s from %u cycle(s)\n" , "[compresslist]" , type , maxarcp -> arc_parentp -> name , maxarcp -> arc_count , maxarcp -> arc_childp -> name , maxarcp -> arc_cyclecnt ); } # endif /* DEBUG */ printf( "\t%s to %s with %ld calls\n" , maxarcp -> arc_parentp -> name , maxarcp -> arc_childp -> name , maxarcp -> arc_count ); prev = &cyclehead; for ( clp = cyclehead ; clp ; ) { endlist = &clp -> list[ clp -> size ]; for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ ) if ( (*arcpp) -> arc_flags & DEADARC ) break; if ( arcpp == endlist ) { prev = &clp -> next; clp = clp -> next; continue; } for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ ) (*arcpp) -> arc_cyclecnt--; cyclecnt--; *prev = clp -> next; clp = clp -> next; free( clp ); } } #ifdef DEBUG void printsubcycle(cltype *clp) { arctype **arcpp; arctype **endlist; arcpp = clp -> list; printf( "%s \n" , (*arcpp) -> arc_parentp -> name , (*arcpp) -> arc_parentp -> cycleno ) ; for ( endlist = &clp -> list[ clp -> size ]; arcpp < endlist ; arcpp++ ) printf( "\t(%ld) -> %s\n" , (*arcpp) -> arc_count , (*arcpp) -> arc_childp -> name ) ; } #endif /* DEBUG */ void cycletime(void) { int cycle; nltype *cyclenlp; nltype *childp; for ( cycle = 1 ; cycle <= ncycle ; cycle += 1 ) { cyclenlp = &cyclenl[ cycle ]; for ( childp = cyclenlp -> cnext ; childp ; childp = childp -> cnext ) { if ( childp -> propfraction == 0.0 ) { /* * all members have the same propfraction except those * that were excluded with -E */ continue; } cyclenlp -> time += childp -> time; } cyclenlp -> propself = cyclenlp -> propfraction * cyclenlp -> time; } } /* * in one top to bottom pass over the topologically sorted namelist * propagate: * printflag as the union of parents' printflags * propfraction as the sum of fractional parents' propfractions * and while we're here, sum time for functions. */ void doflags(void) { int index; nltype *childp; nltype *oldhead; oldhead = 0; for ( index = nname-1 ; index >= 0 ; index -= 1 ) { childp = topsortnlp[ index ]; /* * if we haven't done this function or cycle, * inherit things from parent. * this way, we are linear in the number of arcs * since we do all members of a cycle (and the cycle itself) * as we hit the first member of the cycle. */ if ( childp -> cyclehead != oldhead ) { oldhead = childp -> cyclehead; inheritflags( childp ); } # ifdef DEBUG if ( debug & PROPDEBUG ) { printf( "[doflags] " ); printname( childp ); printf( " inherits printflag %d and propfraction %f\n" , childp -> printflag , childp -> propfraction ); } # endif /* DEBUG */ if ( ! childp -> printflag ) { /* * printflag is off * it gets turned on by * being on -f list, * or there not being any -f list and not being on -e list. */ if ( onlist( flist , childp -> name ) || ( !fflag && !onlist( elist , childp -> name ) ) ) { childp -> printflag = TRUE; } } else { /* * this function has printing parents: * maybe someone wants to shut it up * by putting it on -e list. (but favor -f over -e) */ if ( ( !onlist( flist , childp -> name ) ) && onlist( elist , childp -> name ) ) { childp -> printflag = FALSE; } } if ( childp -> propfraction == 0.0 ) { /* * no parents to pass time to. * collect time from children if * its on -F list, * or there isn't any -F list and its not on -E list. */ if ( onlist( Flist , childp -> name ) || ( !Fflag && !onlist( Elist , childp -> name ) ) ) { childp -> propfraction = 1.0; } } else { /* * it has parents to pass time to, * but maybe someone wants to shut it up * by putting it on -E list. (but favor -F over -E) */ if ( !onlist( Flist , childp -> name ) && onlist( Elist , childp -> name ) ) { childp -> propfraction = 0.0; } } childp -> propself = childp -> time * childp -> propfraction; printtime += childp -> propself; # ifdef DEBUG if ( debug & PROPDEBUG ) { printf( "[doflags] " ); printname( childp ); printf( " ends up with printflag %d and propfraction %f\n" , childp -> printflag , childp -> propfraction ); printf( "time %f propself %f printtime %f\n" , childp -> time , childp -> propself , printtime ); } # endif /* DEBUG */ } } /* * check if any parent of this child * (or outside parents of this cycle) * have their print flags on and set the * print flag of the child (cycle) appropriately. * similarly, deal with propagation fractions from parents. */ void inheritflags(nltype *childp) { nltype *headp; arctype *arcp; nltype *parentp; nltype *memp; headp = childp -> cyclehead; if ( childp == headp ) { /* * just a regular child, check its parents */ childp -> printflag = FALSE; childp -> propfraction = 0.0; for (arcp = childp -> parents ; arcp ; arcp = arcp -> arc_parentlist) { parentp = arcp -> arc_parentp; if ( childp == parentp ) { continue; } childp -> printflag |= parentp -> printflag; /* * if the child was never actually called * (e.g. this arc is static (and all others are, too)) * no time propagates along this arc. */ if ( arcp -> arc_flags & DEADARC ) { continue; } if ( childp -> npropcall ) { childp -> propfraction += parentp -> propfraction * ( ( (double) arcp -> arc_count ) / ( (double) childp -> npropcall ) ); } } } else { /* * its a member of a cycle, look at all parents from * outside the cycle */ headp -> printflag = FALSE; headp -> propfraction = 0.0; for ( memp = headp -> cnext ; memp ; memp = memp -> cnext ) { for (arcp = memp->parents ; arcp ; arcp = arcp->arc_parentlist) { if ( arcp -> arc_parentp -> cyclehead == headp ) { continue; } parentp = arcp -> arc_parentp; headp -> printflag |= parentp -> printflag; /* * if the cycle was never actually called * (e.g. this arc is static (and all others are, too)) * no time propagates along this arc. */ if ( arcp -> arc_flags & DEADARC ) { continue; } if ( headp -> npropcall ) { headp -> propfraction += parentp -> propfraction * ( ( (double) arcp -> arc_count ) / ( (double) headp -> npropcall ) ); } } } for ( memp = headp ; memp ; memp = memp -> cnext ) { memp -> printflag = headp -> printflag; memp -> propfraction = headp -> propfraction; } } } diff --git a/usr.bin/gprof/dfn.c b/usr.bin/gprof/dfn.c index 081a245c544c..b0bafc18fb52 100644 --- a/usr.bin/gprof/dfn.c +++ b/usr.bin/gprof/dfn.c @@ -1,318 +1,315 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include #include #include "gprof.h" #define DFN_DEPTH 100 struct dfnstruct { nltype *nlentryp; int cycletop; }; typedef struct dfnstruct dfntype; dfntype dfn_stack[ DFN_DEPTH ]; int dfn_depth; int dfn_counter; void dfn_init(void) { dfn_depth = 0; dfn_counter = DFN_NAN; } /* * given this parent, depth first number its children. */ void dfn(nltype *parentp) { arctype *arcp; # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "[dfn] dfn(" ); printname( parentp ); printf( ")\n" ); } # endif /* DEBUG */ /* * if we're already numbered, no need to look any further. */ if ( dfn_numbered( parentp ) ) { return; } /* * if we're already busy, must be a cycle */ if ( dfn_busy( parentp ) ) { dfn_findcycle( parentp ); return; } /* * visit yourself before your children */ dfn_pre_visit( parentp ); /* * visit children */ for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) { if ( arcp -> arc_flags & DEADARC ) continue; dfn( arcp -> arc_childp ); } /* * visit yourself after your children */ dfn_post_visit( parentp ); } /* * push a parent onto the stack and mark it busy */ void dfn_pre_visit(nltype *parentp) { dfn_depth += 1; if ( dfn_depth >= DFN_DEPTH ) errx( 1 , "[dfn] out of my depth (dfn_stack overflow)" ); dfn_stack[ dfn_depth ].nlentryp = parentp; dfn_stack[ dfn_depth ].cycletop = dfn_depth; parentp -> toporder = DFN_BUSY; # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "[dfn_pre_visit]\t\t%d:" , dfn_depth ); printname( parentp ); printf( "\n" ); } # endif /* DEBUG */ } /* * are we already numbered? */ bool dfn_numbered(nltype *childp) { return ( childp -> toporder != DFN_NAN && childp -> toporder != DFN_BUSY ); } /* * are we already busy? */ bool dfn_busy(nltype *childp) { if ( childp -> toporder == DFN_NAN ) { return FALSE; } return TRUE; } /* * MISSING: an explanation */ void dfn_findcycle(nltype *childp) { int cycletop; nltype *cycleheadp; nltype *tailp; int index; for ( cycletop = dfn_depth ; cycletop > 0 ; cycletop -= 1 ) { cycleheadp = dfn_stack[ cycletop ].nlentryp; if ( childp == cycleheadp ) { break; } if ( childp -> cyclehead != childp && childp -> cyclehead == cycleheadp ) { break; } } if ( cycletop <= 0 ) errx( 1 , "[dfn_findcycle] couldn't find head of cycle" ); # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "[dfn_findcycle] dfn_depth %d cycletop %d " , dfn_depth , cycletop ); printname( cycleheadp ); printf( "\n" ); } # endif /* DEBUG */ if ( cycletop == dfn_depth ) { /* * this is previous function, e.g. this calls itself * sort of boring */ dfn_self_cycle( childp ); } else { /* * glom intervening functions that aren't already * glommed into this cycle. * things have been glommed when their cyclehead field * points to the head of the cycle they are glommed into. */ for ( tailp = cycleheadp ; tailp -> cnext ; tailp = tailp -> cnext ) { /* void: chase down to tail of things already glommed */ # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "[dfn_findcycle] tail " ); printname( tailp ); printf( "\n" ); } # endif /* DEBUG */ } /* * if what we think is the top of the cycle * has a cyclehead field, then it's not really the * head of the cycle, which is really what we want */ if ( cycleheadp -> cyclehead != cycleheadp ) { cycleheadp = cycleheadp -> cyclehead; # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "[dfn_findcycle] new cyclehead " ); printname( cycleheadp ); printf( "\n" ); } # endif /* DEBUG */ } for ( index = cycletop + 1 ; index <= dfn_depth ; index += 1 ) { childp = dfn_stack[ index ].nlentryp; if ( childp -> cyclehead == childp ) { /* * not yet glommed anywhere, glom it * and fix any children it has glommed */ tailp -> cnext = childp; childp -> cyclehead = cycleheadp; # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "[dfn_findcycle] glomming " ); printname( childp ); printf( " onto " ); printname( cycleheadp ); printf( "\n" ); } # endif /* DEBUG */ for ( tailp = childp ; tailp->cnext ; tailp = tailp->cnext ) { tailp -> cnext -> cyclehead = cycleheadp; # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "[dfn_findcycle] and its tail " ); printname( tailp -> cnext ); printf( " onto " ); printname( cycleheadp ); printf( "\n" ); } # endif /* DEBUG */ } } else if ( childp -> cyclehead != cycleheadp /* firewall */ ) { fprintf( stderr , "[dfn_busy] glommed, but not to cyclehead\n" ); } } } } /* * deal with self-cycles * for lint: ARGSUSED */ void dfn_self_cycle(nltype *parentp) { /* * since we are taking out self-cycles elsewhere * no need for the special case, here. */ # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "[dfn_self_cycle] " ); printname( parentp ); printf( "\n" ); } # endif /* DEBUG */ } /* * visit a node after all its children * [MISSING: an explanation] * and pop it off the stack */ void dfn_post_visit(nltype *parentp) { nltype *memberp; # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "[dfn_post_visit]\t%d: " , dfn_depth ); printname( parentp ); printf( "\n" ); } # endif /* DEBUG */ /* * number functions and things in their cycles * unless the function is itself part of a cycle */ if ( parentp -> cyclehead == parentp ) { dfn_counter += 1; for ( memberp = parentp ; memberp ; memberp = memberp -> cnext ) { memberp -> toporder = dfn_counter; # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "[dfn_post_visit]\t\tmember " ); printname( memberp ); printf( " -> toporder = %d\n" , dfn_counter ); } # endif /* DEBUG */ } } else { # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "[dfn_post_visit]\t\tis part of a cycle\n" ); } # endif /* DEBUG */ } dfn_depth -= 1; } diff --git a/usr.bin/gprof/elf.c b/usr.bin/gprof/elf.c index f46bc07524f5..a237e1dd4c02 100644 --- a/usr.bin/gprof/elf.c +++ b/usr.bin/gprof/elf.c @@ -1,141 +1,140 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 /* From: */ #endif -#include #include #include #include #include #include #include #include #include #include "gprof.h" static bool wantsym(const Elf_Sym *, const char *); /* Things which get -E excluded by default. */ static char *excludes[] = { ".mcount", "_mcleanup", NULL }; int elf_getnfile(const char *filename, char ***defaultEs) { int fd; Elf_Ehdr h; struct stat s; void *mapbase; const char *base; const Elf_Shdr *shdrs; const Elf_Shdr *sh_symtab; const Elf_Shdr *sh_strtab; const char *strtab; const Elf_Sym *symtab; int symtabct; int i; if ((fd = open(filename, O_RDONLY)) == -1) err(1, "%s", filename); if (read(fd, &h, sizeof h) != sizeof h || !IS_ELF(h)) { close(fd); return -1; } if (fstat(fd, &s) == -1) err(1, "cannot fstat %s", filename); if ((mapbase = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) err(1, "cannot mmap %s", filename); close(fd); base = (const char *)mapbase; shdrs = (const Elf_Shdr *)(base + h.e_shoff); /* Find the symbol table and associated string table section. */ for (i = 1; i < h.e_shnum; i++) if (shdrs[i].sh_type == SHT_SYMTAB) break; if (i == h.e_shnum) errx(1, "%s has no symbol table", filename); sh_symtab = &shdrs[i]; sh_strtab = &shdrs[sh_symtab->sh_link]; symtab = (const Elf_Sym *)(base + sh_symtab->sh_offset); symtabct = sh_symtab->sh_size / sh_symtab->sh_entsize; strtab = (const char *)(base + sh_strtab->sh_offset); /* Count the symbols that we're interested in. */ nname = 0; for (i = 1; i < symtabct; i++) if (wantsym(&symtab[i], strtab)) nname++; /* Allocate memory for them, plus a terminating entry. */ if ((nl = (nltype *)calloc(nname + 1, sizeof(nltype))) == NULL) errx(1, "insufficient memory for symbol table"); /* Read them in. */ npe = nl; for (i = 1; i < symtabct; i++) { const Elf_Sym *sym = &symtab[i]; if (wantsym(sym, strtab)) { npe->value = sym->st_value; npe->name = strtab + sym->st_name; npe++; } } npe->value = -1; *defaultEs = excludes; return 0; } static bool wantsym(const Elf_Sym *sym, const char *strtab) { int type; int bind; type = ELF_ST_TYPE(sym->st_info); bind = ELF_ST_BIND(sym->st_info); if (type != STT_FUNC || (aflag && bind == STB_LOCAL) || (uflag && strchr(strtab + sym->st_name, '.') != NULL)) return 0; return 1; } diff --git a/usr.bin/gprof/hertz.c b/usr.bin/gprof/hertz.c index 9717b1b6253b..2d1b6edca52a 100644 --- a/usr.bin/gprof/hertz.c +++ b/usr.bin/gprof/hertz.c @@ -1,60 +1,57 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include #include /* * discover the tick frequency of the machine * if something goes wrong, we return 0, an impossible hertz. */ #define HZ_WRONG 0 int hertz(void); int hertz(void) { struct itimerval tim; tim.it_interval.tv_sec = 0; tim.it_interval.tv_usec = 1; tim.it_value.tv_sec = 0; tim.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &tim, 0); setitimer(ITIMER_REAL, 0, &tim); if (tim.it_interval.tv_usec < 2) return(HZ_WRONG); return (1000000 / tim.it_interval.tv_usec); } diff --git a/usr.bin/gprof/lookup.c b/usr.bin/gprof/lookup.c index 00445ea86a45..1d53744f59cc 100644 --- a/usr.bin/gprof/lookup.c +++ b/usr.bin/gprof/lookup.c @@ -1,116 +1,113 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include #include "gprof.h" /* * look up an address in a sorted-by-address namelist * this deals with misses by mapping them to the next lower * entry point. */ nltype * nllookup(unsigned long address) { register long low; register long middle; register long high; # ifdef DEBUG register int probes; probes = 0; # endif /* DEBUG */ for ( low = 0 , high = nname - 1 ; low != high ; ) { # ifdef DEBUG probes += 1; # endif /* DEBUG */ middle = ( high + low ) >> 1; if ( nl[ middle ].value <= address && nl[ middle+1 ].value > address ) { # ifdef DEBUG if ( debug & LOOKUPDEBUG ) { printf( "[nllookup] %d (%d) probes\n" , probes , nname-1 ); } # endif /* DEBUG */ #if defined(__arm__) if (nl[middle].name[0] == '$' && nl[middle-1].value == nl[middle].value) middle--; #endif return &nl[ middle ]; } if ( nl[ middle ].value > address ) { high = middle; } else { low = middle + 1; } } # ifdef DEBUG if ( debug & LOOKUPDEBUG ) { fprintf( stderr , "[nllookup] (%d) binary search fails\n" , nname-1 ); } # endif /* DEBUG */ return 0; } arctype * arclookup(nltype *parentp, nltype *childp) { arctype *arcp; if ( parentp == 0 || childp == 0 ) { fprintf( stderr, "[arclookup] parentp == 0 || childp == 0\n" ); return 0; } # ifdef DEBUG if ( debug & LOOKUPDEBUG ) { printf( "[arclookup] parent %s child %s\n" , parentp -> name , childp -> name ); } # endif /* DEBUG */ for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) { # ifdef DEBUG if ( debug & LOOKUPDEBUG ) { printf( "[arclookup]\t arc_parent %s arc_child %s\n" , arcp -> arc_parentp -> name , arcp -> arc_childp -> name ); } # endif /* DEBUG */ if ( arcp -> arc_childp == childp ) { return arcp; } } return 0; } diff --git a/usr.bin/gprof/printgprof.c b/usr.bin/gprof/printgprof.c index 92e48a89b497..07791bf2d154 100644 --- a/usr.bin/gprof/printgprof.c +++ b/usr.bin/gprof/printgprof.c @@ -1,743 +1,740 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include #include #include #include "gprof.h" #include "pathnames.h" int namecmp(const void *, const void *); int timecmp(const void *, const void *); void printprof(void) { register nltype *np; nltype **sortednlp; int idx; actime = 0.0; printf( "\f\n" ); flatprofheader(); /* * Sort the symbol table in by time */ sortednlp = (nltype **) calloc( nname , sizeof(nltype *) ); if ( sortednlp == (nltype **) 0 ) errx( 1 , "[printprof] ran out of memory for time sorting" ); for ( idx = 0 ; idx < nname ; idx += 1 ) { sortednlp[ idx ] = &nl[ idx ]; } qsort( sortednlp , nname , sizeof(nltype *) , timecmp ); for ( idx = 0 ; idx < nname ; idx += 1 ) { np = sortednlp[ idx ]; flatprofline( np ); } actime = 0.0; free( sortednlp ); } int timecmp(const void *v1, const void *v2) { const nltype **npp1 = (const nltype **)v1; const nltype **npp2 = (const nltype **)v2; double timediff; long calldiff; timediff = (*npp2) -> time - (*npp1) -> time; if ( timediff > 0.0 ) return 1 ; if ( timediff < 0.0 ) return -1; calldiff = (*npp2) -> ncall - (*npp1) -> ncall; if ( calldiff > 0 ) return 1; if ( calldiff < 0 ) return -1; return( strcmp( (*npp1) -> name , (*npp2) -> name ) ); } /* * header for flatprofline */ void flatprofheader(void) { if ( bflag ) { printblurb( _PATH_FLAT_BLURB ); } printf( "\ngranularity: each sample hit covers %g byte(s)" , scale * HISTORICAL_SCALE_2 ); if ( totime > 0.0 ) { printf( " for %.2f%% of %.2f seconds\n\n" , 100.0/totime , totime / hz ); } else { printf( " no time accumulated\n\n" ); /* * this doesn't hurt since all the numerators will be zero. */ totime = 1.0; } printf( "%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s %-8.8s\n" , "% " , "cumulative" , "self " , "" , "self " , "total " , "" ); printf( "%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s %-8.8s\n" , "time" , "seconds " , "seconds" , "calls" , hz >= 10000000 ? "ns/call" : hz >= 10000 ? "us/call" : "ms/call" , hz >= 10000000 ? "ns/call" : hz >= 10000 ? "us/call" : "ms/call" , "name" ); } void flatprofline(register nltype *np) { if ( zflag == 0 && np -> ncall == 0 && np -> time == 0 && np -> childtime == 0 ) { return; } actime += np -> time; if (hz >= 10000) printf( "%5.1f %10.3f %8.3f" , 100 * np -> time / totime , actime / hz , np -> time / hz ); else printf( "%5.1f %10.2f %8.2f" , 100 * np -> time / totime , actime / hz , np -> time / hz ); if ( np -> ncall != 0 ) { if (hz >= 10000000) printf( " %8ld %8.0f %8.0f " , np -> ncall , 1e9 * np -> time / hz / np -> ncall , 1e9 * ( np -> time + np -> childtime ) / hz / np -> ncall ); else if (hz >= 10000) printf( " %8ld %8.0f %8.0f " , np -> ncall , 1e6 * np -> time / hz / np -> ncall , 1e6 * ( np -> time + np -> childtime ) / hz / np -> ncall ); else printf( " %8ld %8.2f %8.2f " , np -> ncall , 1000 * np -> time / hz / np -> ncall , 1000 * ( np -> time + np -> childtime ) / hz / np -> ncall ); } else if ( np -> time != 0 || np -> childtime != 0 ) { printf( " %8ld %7.2f%% %8.8s " , np -> ncall , 100 * np -> time / ( np -> time + np -> childtime ) , "" ); } else { printf( " %8.8s %8.8s %8.8s " , "" , "" , "" ); } printname( np ); printf( "\n" ); } void gprofheader(void) { if ( bflag ) { printblurb( _PATH_CALLG_BLURB ); } printf( "\ngranularity: each sample hit covers %g byte(s)" , scale * HISTORICAL_SCALE_2 ); if ( printtime > 0.0 ) { printf( " for %.2f%% of %.2f seconds\n\n" , 100.0/printtime , printtime / hz ); } else { printf( " no time propagated\n\n" ); /* * this doesn't hurt, since all the numerators will be 0.0 */ printtime = 1.0; } printf( "%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n" , "" , "" , "" , "" , "called" , "total" , "parents"); printf( "%-6.6s %5.5s %7.7s %11.11s %7.7s+%-7.7s %-8.8s\t%5.5s\n" , "index" , "%time" , "self" , "descendents" , "called" , "self" , "name" , "index" ); printf( "%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n" , "" , "" , "" , "" , "called" , "total" , "children"); printf( "\n" ); } void gprofline(register nltype *np) { char kirkbuffer[ BUFSIZ ]; sprintf( kirkbuffer , "[%d]" , np -> index ); printf( "%-6.6s %5.1f %7.2f %11.2f" , kirkbuffer , 100 * ( np -> propself + np -> propchild ) / printtime , np -> propself / hz , np -> propchild / hz ); if ( ( np -> ncall + np -> selfcalls ) != 0 ) { printf( " %7ld" , np -> npropcall ); if ( np -> selfcalls != 0 ) { printf( "+%-7ld " , np -> selfcalls ); } else { printf( " %7.7s " , "" ); } } else { printf( " %7.7s %7.7s " , "" , "" ); } printname( np ); printf( "\n" ); } void printgprof(nltype **timesortnlp) { int idx; nltype *parentp; /* * Print out the structured profiling list */ gprofheader(); for ( idx = 0 ; idx < nname + ncycle ; idx ++ ) { parentp = timesortnlp[ idx ]; if ( zflag == 0 && parentp -> ncall == 0 && parentp -> selfcalls == 0 && parentp -> propself == 0 && parentp -> propchild == 0 ) { continue; } if ( ! parentp -> printflag ) { continue; } if ( parentp -> name == 0 && parentp -> cycleno != 0 ) { /* * cycle header */ printcycle( parentp ); printmembers( parentp ); } else { printparents( parentp ); gprofline( parentp ); printchildren( parentp ); } printf( "\n" ); printf( "-----------------------------------------------\n" ); printf( "\n" ); } free( timesortnlp ); } /* * sort by decreasing propagated time * if times are equal, but one is a cycle header, * say that's first (e.g. less, i.e. -1). * if one's name doesn't have an underscore and the other does, * say the one is first. * all else being equal, sort by names. */ int totalcmp(const void *v1, const void *v2) { const nltype **npp1 = (const nltype **)v1; const nltype **npp2 = (const nltype **)v2; register const nltype *np1 = *npp1; register const nltype *np2 = *npp2; double diff; diff = ( np1 -> propself + np1 -> propchild ) - ( np2 -> propself + np2 -> propchild ); if ( diff < 0.0 ) return 1; if ( diff > 0.0 ) return -1; if ( np1 -> name == 0 && np1 -> cycleno != 0 ) return -1; if ( np2 -> name == 0 && np2 -> cycleno != 0 ) return 1; if ( np1 -> name == 0 ) return -1; if ( np2 -> name == 0 ) return 1; if ( *(np1 -> name) != '_' && *(np2 -> name) == '_' ) return -1; if ( *(np1 -> name) == '_' && *(np2 -> name) != '_' ) return 1; if ( np1 -> ncall > np2 -> ncall ) return -1; if ( np1 -> ncall < np2 -> ncall ) return 1; return strcmp( np1 -> name , np2 -> name ); } void printparents(nltype *childp) { nltype *parentp; arctype *arcp; nltype *cycleheadp; if ( childp -> cyclehead != 0 ) { cycleheadp = childp -> cyclehead; } else { cycleheadp = childp; } if ( childp -> parents == 0 ) { printf( "%6.6s %5.5s %7.7s %11.11s %7.7s %7.7s \n" , "" , "" , "" , "" , "" , "" ); return; } sortparents( childp ); for ( arcp = childp -> parents ; arcp ; arcp = arcp -> arc_parentlist ) { parentp = arcp -> arc_parentp; if ( childp == parentp || ( arcp -> arc_flags & DEADARC ) || ( childp->cycleno != 0 && parentp->cycleno == childp->cycleno ) ) { /* * selfcall or call among siblings */ printf( "%6.6s %5.5s %7.7s %11.11s %7ld %7.7s " , "" , "" , "" , "" , arcp -> arc_count , "" ); printname( parentp ); printf( "\n" ); } else { /* * regular parent of child */ printf( "%6.6s %5.5s %7.2f %11.2f %7ld/%-7ld " , "" , "" , arcp -> arc_time / hz , arcp -> arc_childtime / hz , arcp -> arc_count , cycleheadp -> npropcall ); printname( parentp ); printf( "\n" ); } } } void printchildren(nltype *parentp) { nltype *childp; arctype *arcp; sortchildren( parentp ); arcp = parentp -> children; for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) { childp = arcp -> arc_childp; if ( childp == parentp || ( arcp -> arc_flags & DEADARC ) || ( childp->cycleno != 0 && childp->cycleno == parentp->cycleno ) ) { /* * self call or call to sibling */ printf( "%6.6s %5.5s %7.7s %11.11s %7ld %7.7s " , "" , "" , "" , "" , arcp -> arc_count , "" ); printname( childp ); printf( "\n" ); } else { /* * regular child of parent */ printf( "%6.6s %5.5s %7.2f %11.2f %7ld/%-7ld " , "" , "" , arcp -> arc_time / hz , arcp -> arc_childtime / hz , arcp -> arc_count , childp -> cyclehead -> npropcall ); printname( childp ); printf( "\n" ); } } } void printname(nltype *selfp) { if ( selfp -> name != 0 ) { printf( "%s" , selfp -> name ); # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "{%d} " , selfp -> toporder ); } if ( debug & PROPDEBUG ) { printf( "%5.2f%% " , selfp -> propfraction ); } # endif /* DEBUG */ } if ( selfp -> cycleno != 0 ) { printf( " " , selfp -> cycleno ); } if ( selfp -> index != 0 ) { if ( selfp -> printflag ) { printf( " [%d]" , selfp -> index ); } else { printf( " (%d)" , selfp -> index ); } } } void sortchildren(nltype *parentp) { arctype *arcp; arctype *detachedp; arctype sorted; arctype *prevp; /* * unlink children from parent, * then insertion sort back on to sorted's children. * *arcp the arc you have detached and are inserting. * *detachedp the rest of the arcs to be sorted. * sorted arc list onto which you insertion sort. * *prevp arc before the arc you are comparing. */ sorted.arc_childlist = 0; for ( (arcp = parentp -> children)&&(detachedp = arcp -> arc_childlist); arcp ; (arcp = detachedp)&&(detachedp = detachedp -> arc_childlist)) { /* * consider *arcp as disconnected * insert it into sorted */ for ( prevp = &sorted ; prevp -> arc_childlist ; prevp = prevp -> arc_childlist ) { if ( arccmp( arcp , prevp -> arc_childlist ) != LESSTHAN ) { break; } } arcp -> arc_childlist = prevp -> arc_childlist; prevp -> arc_childlist = arcp; } /* * reattach sorted children to parent */ parentp -> children = sorted.arc_childlist; } void sortparents(nltype *childp) { arctype *arcp; arctype *detachedp; arctype sorted; arctype *prevp; /* * unlink parents from child, * then insertion sort back on to sorted's parents. * *arcp the arc you have detached and are inserting. * *detachedp the rest of the arcs to be sorted. * sorted arc list onto which you insertion sort. * *prevp arc before the arc you are comparing. */ sorted.arc_parentlist = 0; for ( (arcp = childp -> parents)&&(detachedp = arcp -> arc_parentlist); arcp ; (arcp = detachedp)&&(detachedp = detachedp -> arc_parentlist)) { /* * consider *arcp as disconnected * insert it into sorted */ for ( prevp = &sorted ; prevp -> arc_parentlist ; prevp = prevp -> arc_parentlist ) { if ( arccmp( arcp , prevp -> arc_parentlist ) != GREATERTHAN ) { break; } } arcp -> arc_parentlist = prevp -> arc_parentlist; prevp -> arc_parentlist = arcp; } /* * reattach sorted arcs to child */ childp -> parents = sorted.arc_parentlist; } /* * print a cycle header */ void printcycle(nltype *cyclep) { char kirkbuffer[ BUFSIZ ]; sprintf( kirkbuffer , "[%d]" , cyclep -> index ); printf( "%-6.6s %5.1f %7.2f %11.2f %7ld" , kirkbuffer , 100 * ( cyclep -> propself + cyclep -> propchild ) / printtime , cyclep -> propself / hz , cyclep -> propchild / hz , cyclep -> npropcall ); if ( cyclep -> selfcalls != 0 ) { printf( "+%-7ld" , cyclep -> selfcalls ); } else { printf( " %7.7s" , "" ); } printf( " \t[%d]\n" , cyclep -> cycleno , cyclep -> index ); } /* * print the members of a cycle */ void printmembers(nltype *cyclep) { nltype *memberp; sortmembers( cyclep ); for ( memberp = cyclep -> cnext ; memberp ; memberp = memberp -> cnext ) { printf( "%6.6s %5.5s %7.2f %11.2f %7ld" , "" , "" , memberp -> propself / hz , memberp -> propchild / hz , memberp -> npropcall ); if ( memberp -> selfcalls != 0 ) { printf( "+%-7ld" , memberp -> selfcalls ); } else { printf( " %7.7s" , "" ); } printf( " " ); printname( memberp ); printf( "\n" ); } } /* * sort members of a cycle */ void sortmembers(nltype *cyclep) { nltype *todo; nltype *doing; nltype *prev; /* * detach cycle members from cyclehead, * and insertion sort them back on. */ todo = cyclep -> cnext; cyclep -> cnext = 0; for ( (doing = todo)&&(todo = doing -> cnext); doing ; (doing = todo )&&(todo = doing -> cnext )){ for ( prev = cyclep ; prev -> cnext ; prev = prev -> cnext ) { if ( membercmp( doing , prev -> cnext ) == GREATERTHAN ) { break; } } doing -> cnext = prev -> cnext; prev -> cnext = doing; } } /* * major sort is on propself + propchild, * next is sort on ncalls + selfcalls. */ int membercmp(nltype *this, nltype *that) { double thistime = this -> propself + this -> propchild; double thattime = that -> propself + that -> propchild; long thiscalls = this -> ncall + this -> selfcalls; long thatcalls = that -> ncall + that -> selfcalls; if ( thistime > thattime ) { return GREATERTHAN; } if ( thistime < thattime ) { return LESSTHAN; } if ( thiscalls > thatcalls ) { return GREATERTHAN; } if ( thiscalls < thatcalls ) { return LESSTHAN; } return EQUALTO; } /* * compare two arcs to/from the same child/parent. * - if one arc is a self arc, it's least. * - if one arc is within a cycle, it's less than. * - if both arcs are within a cycle, compare arc counts. * - if neither arc is within a cycle, compare with * arc_time + arc_childtime as major key * arc count as minor key */ int arccmp(arctype *thisp, arctype *thatp) { nltype *thisparentp = thisp -> arc_parentp; nltype *thischildp = thisp -> arc_childp; nltype *thatparentp = thatp -> arc_parentp; nltype *thatchildp = thatp -> arc_childp; double thistime; double thattime; # ifdef DEBUG if ( debug & TIMEDEBUG ) { printf( "[arccmp] " ); printname( thisparentp ); printf( " calls " ); printname ( thischildp ); printf( " %f + %f %ld/%ld\n" , thisp -> arc_time , thisp -> arc_childtime , thisp -> arc_count , thischildp -> ncall ); printf( "[arccmp] " ); printname( thatparentp ); printf( " calls " ); printname( thatchildp ); printf( " %f + %f %ld/%ld\n" , thatp -> arc_time , thatp -> arc_childtime , thatp -> arc_count , thatchildp -> ncall ); printf( "\n" ); } # endif /* DEBUG */ if ( thisparentp == thischildp ) { /* this is a self call */ return LESSTHAN; } if ( thatparentp == thatchildp ) { /* that is a self call */ return GREATERTHAN; } if ( thisparentp -> cycleno != 0 && thischildp -> cycleno != 0 && thisparentp -> cycleno == thischildp -> cycleno ) { /* this is a call within a cycle */ if ( thatparentp -> cycleno != 0 && thatchildp -> cycleno != 0 && thatparentp -> cycleno == thatchildp -> cycleno ) { /* that is a call within the cycle, too */ if ( thisp -> arc_count < thatp -> arc_count ) { return LESSTHAN; } if ( thisp -> arc_count > thatp -> arc_count ) { return GREATERTHAN; } return EQUALTO; } else { /* that isn't a call within the cycle */ return LESSTHAN; } } else { /* this isn't a call within a cycle */ if ( thatparentp -> cycleno != 0 && thatchildp -> cycleno != 0 && thatparentp -> cycleno == thatchildp -> cycleno ) { /* that is a call within a cycle */ return GREATERTHAN; } else { /* neither is a call within a cycle */ thistime = thisp -> arc_time + thisp -> arc_childtime; thattime = thatp -> arc_time + thatp -> arc_childtime; if ( thistime < thattime ) return LESSTHAN; if ( thistime > thattime ) return GREATERTHAN; if ( thisp -> arc_count < thatp -> arc_count ) return LESSTHAN; if ( thisp -> arc_count > thatp -> arc_count ) return GREATERTHAN; return EQUALTO; } } } void printblurb(const char *blurbname) { FILE *blurbfile; int input; blurbfile = fopen( blurbname , "r" ); if ( blurbfile == NULL ) { warn( "%s" , blurbname ); return; } while ( ( input = getc( blurbfile ) ) != EOF ) { putchar( input ); } fclose( blurbfile ); } int namecmp(const void *v1, const void *v2) { const nltype **npp1 = (const nltype **)v1; const nltype **npp2 = (const nltype **)v2; return( strcmp( (*npp1) -> name , (*npp2) -> name ) ); } void printindex(void) { nltype **namesortnlp; register nltype *nlp; int idx, nnames, todo, i, j; char peterbuffer[ BUFSIZ ]; /* * Now, sort regular function name alphabetically * to create an index. */ namesortnlp = (nltype **) calloc( nname + ncycle , sizeof(nltype *) ); if ( namesortnlp == (nltype **) 0 ) errx( 1 , "ran out of memory for sorting"); for ( idx = 0 , nnames = 0 ; idx < nname ; idx++ ) { if ( zflag == 0 && nl[idx].ncall == 0 && nl[idx].time == 0 ) continue; namesortnlp[nnames++] = &nl[idx]; } qsort( namesortnlp , nnames , sizeof(nltype *) , namecmp ); for ( idx = 1 , todo = nnames ; idx <= ncycle ; idx++ ) { namesortnlp[todo++] = &cyclenl[idx]; } printf( "\f\nIndex by function name\n\n" ); idx = ( todo + 2 ) / 3; for ( i = 0; i < idx ; i++ ) { for ( j = i; j < todo ; j += idx ) { nlp = namesortnlp[ j ]; if ( nlp -> printflag ) { sprintf( peterbuffer , "[%d]" , nlp -> index ); } else { sprintf( peterbuffer , "(%d)" , nlp -> index ); } if ( j < nnames ) { printf( "%6.6s %-19.19s" , peterbuffer , nlp -> name ); } else { printf( "%6.6s " , peterbuffer ); sprintf( peterbuffer , "" , nlp -> cycleno ); printf( "%-19.19s" , peterbuffer ); } } printf( "\n" ); } free( namesortnlp ); } diff --git a/usr.bin/gprof/printlist.c b/usr.bin/gprof/printlist.c index a5d33fc1e0c6..a248aa9f3226 100644 --- a/usr.bin/gprof/printlist.c +++ b/usr.bin/gprof/printlist.c @@ -1,87 +1,84 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include #include #include #include "gprof.h" /* * these are the lists of names: * there is the list head and then the listname * is a pointer to the list head * (for ease of passing to stringlist functions). */ struct stringlist kfromhead = { 0 , 0 }; struct stringlist *kfromlist = &kfromhead; struct stringlist ktohead = { 0 , 0 }; struct stringlist *ktolist = &ktohead; struct stringlist fhead = { 0 , 0 }; struct stringlist *flist = &fhead; struct stringlist Fhead = { 0 , 0 }; struct stringlist *Flist = &Fhead; struct stringlist ehead = { 0 , 0 }; struct stringlist *elist = &ehead; struct stringlist Ehead = { 0 , 0 }; struct stringlist *Elist = &Ehead; void addlist(struct stringlist *listp, char *funcname) { struct stringlist *slp; slp = (struct stringlist *) malloc( sizeof(struct stringlist)); if ( slp == (struct stringlist *) 0 ) errx( 1 , "no room for printlist"); slp -> next = listp -> next; slp -> string = funcname; listp -> next = slp; } bool onlist(struct stringlist *listp, const char *funcname) { struct stringlist *slp; for ( slp = listp -> next ; slp ; slp = slp -> next ) { if ( ! strcmp( slp -> string , funcname ) ) { return TRUE; } if ( funcname[0] == '_' && ! strcmp( slp -> string , &funcname[1] ) ) { return TRUE; } } return FALSE; } diff --git a/usr.bin/grep/file.c b/usr.bin/grep/file.c index 3349b72842ca..0b8240504ce6 100644 --- a/usr.bin/grep/file.c +++ b/usr.bin/grep/file.c @@ -1,249 +1,248 @@ /* $NetBSD: file.c,v 1.5 2011/02/16 18:35:39 joerg Exp $ */ /* $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1999 James Howard and Dag-Erling Smørgrav * Copyright (C) 2008-2010 Gabor Kovesdan * Copyright (C) 2010 Dimitry Andric * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "grep.h" #define MAXBUFSIZ (32 * 1024) #define LNBUFBUMP 80 static char *buffer; static char *bufpos; static size_t bufrem; static size_t fsiz; static char *lnbuf; static size_t lnbuflen; static inline int grep_refill(struct file *f) { ssize_t nr; if (filebehave == FILE_MMAP) return (0); bufpos = buffer; bufrem = 0; nr = read(f->fd, buffer, MAXBUFSIZ); if (nr < 0 && errno == EISDIR) nr = 0; if (nr < 0) return (-1); bufrem = nr; return (0); } static inline int grep_lnbufgrow(size_t newlen) { if (lnbuflen < newlen) { lnbuf = grep_realloc(lnbuf, newlen); lnbuflen = newlen; } return (0); } char * grep_fgetln(struct file *f, struct parsec *pc) { char *p; size_t len; size_t off; ptrdiff_t diff; /* Fill the buffer, if necessary */ if (bufrem == 0 && grep_refill(f) != 0) goto error; if (bufrem == 0) { /* Return zero length to indicate EOF */ pc->ln.len= 0; return (bufpos); } /* Look for a newline in the remaining part of the buffer */ if ((p = memchr(bufpos, fileeol, bufrem)) != NULL) { ++p; /* advance over newline */ len = p - bufpos; if (grep_lnbufgrow(len + 1)) goto error; memcpy(lnbuf, bufpos, len); bufrem -= len; bufpos = p; pc->ln.len = len; lnbuf[len] = '\0'; return (lnbuf); } /* We have to copy the current buffered data to the line buffer */ for (len = bufrem, off = 0; ; len += bufrem) { /* Make sure there is room for more data */ if (grep_lnbufgrow(len + LNBUFBUMP)) goto error; memcpy(lnbuf + off, bufpos, len - off); /* With FILE_MMAP, this is EOF; there's no more to refill */ if (filebehave == FILE_MMAP) { bufrem -= len; break; } off = len; /* Fetch more to try and find EOL/EOF */ if (grep_refill(f) != 0) goto error; if (bufrem == 0) /* EOF: return partial line */ break; if ((p = memchr(bufpos, fileeol, bufrem)) == NULL) continue; /* got it: finish up the line (like code above) */ ++p; diff = p - bufpos; len += diff; if (grep_lnbufgrow(len + 1)) goto error; memcpy(lnbuf + off, bufpos, diff); bufrem -= diff; bufpos = p; break; } pc->ln.len = len; lnbuf[len] = '\0'; return (lnbuf); error: pc->ln.len = 0; return (NULL); } /* * Opens a file for processing. */ struct file * grep_open(const char *path) { struct file *f; f = grep_malloc(sizeof *f); memset(f, 0, sizeof *f); if (path == NULL) { /* Processing stdin implies --line-buffered. */ lbflag = true; f->fd = STDIN_FILENO; } else if ((f->fd = open(path, O_RDONLY)) == -1) goto error1; if (filebehave == FILE_MMAP) { struct stat st; if (fstat(f->fd, &st) == -1 || !S_ISREG(st.st_mode)) filebehave = FILE_STDIO; else { int flags = MAP_PRIVATE | MAP_NOCORE | MAP_NOSYNC; #ifdef MAP_PREFAULT_READ flags |= MAP_PREFAULT_READ; #endif fsiz = st.st_size; buffer = mmap(NULL, fsiz, PROT_READ, flags, f->fd, (off_t)0); if (buffer == MAP_FAILED) filebehave = FILE_STDIO; else { bufrem = st.st_size; bufpos = buffer; madvise(buffer, st.st_size, MADV_SEQUENTIAL); } } } if ((buffer == NULL) || (buffer == MAP_FAILED)) buffer = grep_malloc(MAXBUFSIZ); /* Fill read buffer, also catches errors early */ if (bufrem == 0 && grep_refill(f) != 0) goto error2; /* Check for binary stuff, if necessary */ if (binbehave != BINFILE_TEXT && fileeol != '\0' && memchr(bufpos, '\0', bufrem) != NULL) f->binary = true; return (f); error2: close(f->fd); error1: free(f); return (NULL); } /* * Closes a file. */ void grep_close(struct file *f) { close(f->fd); /* Reset read buffer and line buffer */ if (filebehave == FILE_MMAP) { munmap(buffer, fsiz); buffer = NULL; } bufpos = buffer; bufrem = 0; free(lnbuf); lnbuf = NULL; lnbuflen = 0; } diff --git a/usr.bin/grep/queue.c b/usr.bin/grep/queue.c index 6c4815992d0f..296e19fb0f6d 100644 --- a/usr.bin/grep/queue.c +++ b/usr.bin/grep/queue.c @@ -1,143 +1,142 @@ /* $NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1999 James Howard and Dag-Erling Smørgrav * All rights reserved. * Copyright (c) 2020 Kyle Evans * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * A really poor man's queue. It does only what it has to and gets out of * Dodge. It is used in place of to get a better performance. */ -#include #include #include #include #include #include "grep.h" typedef struct str qentry_t; static long long filled; static qentry_t *qend, *qpool; /* * qnext is the next entry to populate. qlist is where the list actually * starts, for the purposes of printing. */ static qentry_t *qlist, *qnext; void initqueue(void) { qlist = qnext = qpool = grep_calloc(Bflag, sizeof(*qpool)); qend = qpool + (Bflag - 1); } static qentry_t * advqueue(qentry_t *itemp) { if (itemp == qend) return (qpool); return (itemp + 1); } /* * Enqueue another line; return true if we've dequeued a line as a result */ bool enqueue(struct str *x) { qentry_t *item; bool rotated; item = qnext; qnext = advqueue(qnext); rotated = false; if (filled < Bflag) { filled++; } else if (filled == Bflag) { /* We had already filled up coming in; just rotate. */ qlist = advqueue(qlist); rotated = true; free(item->dat); } /* len + 1 for NUL-terminator */ item->dat = grep_malloc(sizeof(char) * x->len + 1); item->len = x->len; item->line_no = x->line_no; item->boff = x->boff; item->off = x->off; memcpy(item->dat, x->dat, x->len); item->dat[x->len] = '\0'; item->file = x->file; return (rotated); } void printqueue(void) { qentry_t *item; item = qlist; do { /* Buffer must have ended early. */ if (item->dat == NULL) break; grep_printline(item, '-'); free(item->dat); item->dat = NULL; item = advqueue(item); } while (item != qlist); qlist = qnext = qpool; filled = 0; } void clearqueue(void) { qentry_t *item; item = qlist; do { free(item->dat); item->dat = NULL; item = advqueue(item); } while (item != qlist); qlist = qnext = qpool; filled = 0; } diff --git a/usr.bin/hexdump/conv.c b/usr.bin/hexdump/conv.c index d4301aa6095c..ddfb0f8c64c4 100644 --- a/usr.bin/hexdump/conv.c +++ b/usr.bin/hexdump/conv.c @@ -1,188 +1,187 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include "hexdump.h" void conv_c(PR *pr, u_char *p, size_t bufsize) { char buf[10]; char const *str; wchar_t wc; size_t clen, oclen; int converr, pad, width; u_char peekbuf[MB_LEN_MAX]; u_char *op; op = NULL; if (pr->mbleft > 0) { str = "**"; pr->mbleft--; goto strpr; } switch(*p) { case '\0': str = "\\0"; goto strpr; /* case '\a': */ case '\007': str = "\\a"; goto strpr; case '\b': str = "\\b"; goto strpr; case '\f': str = "\\f"; goto strpr; case '\n': str = "\\n"; goto strpr; case '\r': str = "\\r"; goto strpr; case '\t': str = "\\t"; goto strpr; case '\v': str = "\\v"; goto strpr; default: break; } /* * Multibyte characters are disabled for hexdump(1) for backwards * compatibility and consistency (none of its other output formats * recognize them correctly). */ converr = 0; if (odmode && MB_CUR_MAX > 1) { oclen = 0; retry: clen = mbrtowc(&wc, p, bufsize, &pr->mbstate); if (clen == 0) clen = 1; else if (clen == (size_t)-1 || (clen == (size_t)-2 && p == peekbuf)) { memset(&pr->mbstate, 0, sizeof(pr->mbstate)); if (p == peekbuf) { /* * We peeked ahead, but that didn't help -- * we either got an illegal sequence or still * can't complete; restore original character. */ oclen = 0; p = op; } wc = *p; clen = 1; converr = 1; } else if (clen == (size_t)-2) { /* * Incomplete character; peek ahead and see if we * can complete it. */ oclen = bufsize; op = p; bufsize = peek(p = peekbuf, MB_CUR_MAX); goto retry; } clen += oclen; } else { wc = *p; clen = 1; } if (!converr && iswprint(wc)) { if (!odmode) { *pr->cchar = 'c'; (void)printf(pr->fmt, (int)wc); } else { *pr->cchar = 'C'; assert(strcmp(pr->fmt, "%3C") == 0); width = wcwidth(wc); assert(width >= 0); pad = 3 - width; if (pad < 0) pad = 0; (void)printf("%*s%C", pad, "", wc); pr->mbleft = clen - 1; } } else { (void)sprintf(buf, "%03o", (int)*p); str = buf; strpr: *pr->cchar = 's'; (void)printf(pr->fmt, str); } } void conv_u(PR *pr, u_char *p) { static char const * list[] = { "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", "bs", "ht", "lf", "vt", "ff", "cr", "so", "si", "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", "can", "em", "sub", "esc", "fs", "gs", "rs", "us", }; /* od used nl, not lf */ if (*p <= 0x1f) { *pr->cchar = 's'; if (odmode && *p == 0x0a) (void)printf(pr->fmt, "nl"); else (void)printf(pr->fmt, list[*p]); } else if (*p == 0x7f) { *pr->cchar = 's'; (void)printf(pr->fmt, "del"); } else if (odmode && *p == 0x20) { /* od replaced space with sp */ *pr->cchar = 's'; (void)printf(pr->fmt, " sp"); } else if (isprint(*p)) { *pr->cchar = 'c'; (void)printf(pr->fmt, *p); } else { *pr->cchar = 'x'; (void)printf(pr->fmt, (int)*p); } } diff --git a/usr.bin/hexdump/display.c b/usr.bin/hexdump/display.c index 224b576ef9f2..2bef03626fe2 100644 --- a/usr.bin/hexdump/display.c +++ b/usr.bin/hexdump/display.c @@ -1,436 +1,433 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hexdump.h" enum _vflag vflag = FIRST; static off_t address; /* address/offset in stream */ static off_t eaddress; /* end address */ static void print(PR *, u_char *); static void noseek(void); void display(void) { FS *fs; FU *fu; PR *pr; int cnt; u_char *bp; off_t saveaddress; u_char savech, *savebp; savech = 0; while ((bp = get())) for (fs = fshead, savebp = bp, saveaddress = address; fs; fs = fs->nextfs, bp = savebp, address = saveaddress) for (fu = fs->nextfu; fu; fu = fu->nextfu) { if (fu->flags&F_IGNORE) break; for (cnt = fu->reps; cnt; --cnt) for (pr = fu->nextpr; pr; address += pr->bcnt, bp += pr->bcnt, pr = pr->nextpr) { if (eaddress && address >= eaddress && !(pr->flags & (F_TEXT|F_BPAD))) bpad(pr); if (cnt == 1 && pr->nospace) { savech = *pr->nospace; *pr->nospace = '\0'; } print(pr, bp); if (cnt == 1 && pr->nospace) *pr->nospace = savech; } } if (endfu) { /* * If eaddress not set, error or file size was multiple of * blocksize, and no partial block ever found. */ if (!eaddress) { if (!address) return; eaddress = address; } for (pr = endfu->nextpr; pr; pr = pr->nextpr) switch(pr->flags) { case F_ADDRESS: (void)printf(pr->fmt, (quad_t)eaddress); break; case F_TEXT: (void)printf("%s", pr->fmt); break; } } } static void print(PR *pr, u_char *bp) { long double ldbl; double f8; float f4; int16_t s2; int32_t s4; int64_t s8; u_int16_t u2; u_int32_t u4; u_int64_t u8; switch(pr->flags) { case F_ADDRESS: (void)printf(pr->fmt, (quad_t)address); break; case F_BPAD: (void)printf(pr->fmt, ""); break; case F_C: conv_c(pr, bp, eaddress ? eaddress - address : blocksize - address % blocksize); break; case F_CHAR: (void)printf(pr->fmt, *bp); break; case F_DBL: switch(pr->bcnt) { case 4: bcopy(bp, &f4, sizeof(f4)); (void)printf(pr->fmt, f4); break; case 8: bcopy(bp, &f8, sizeof(f8)); (void)printf(pr->fmt, f8); break; default: if (pr->bcnt == sizeof(long double)) { bcopy(bp, &ldbl, sizeof(ldbl)); (void)printf(pr->fmt, ldbl); } break; } break; case F_INT: switch(pr->bcnt) { case 1: (void)printf(pr->fmt, (quad_t)(signed char)*bp); break; case 2: bcopy(bp, &s2, sizeof(s2)); (void)printf(pr->fmt, (quad_t)s2); break; case 4: bcopy(bp, &s4, sizeof(s4)); (void)printf(pr->fmt, (quad_t)s4); break; case 8: bcopy(bp, &s8, sizeof(s8)); (void)printf(pr->fmt, s8); break; } break; case F_P: (void)printf(pr->fmt, isprint(*bp) ? *bp : '.'); break; case F_STR: (void)printf(pr->fmt, (char *)bp); break; case F_TEXT: (void)printf("%s", pr->fmt); break; case F_U: conv_u(pr, bp); break; case F_UINT: switch(pr->bcnt) { case 1: (void)printf(pr->fmt, (u_quad_t)*bp); break; case 2: bcopy(bp, &u2, sizeof(u2)); (void)printf(pr->fmt, (u_quad_t)u2); break; case 4: bcopy(bp, &u4, sizeof(u4)); (void)printf(pr->fmt, (u_quad_t)u4); break; case 8: bcopy(bp, &u8, sizeof(u8)); (void)printf(pr->fmt, u8); break; } break; } } void bpad(PR *pr) { static char const *spec = " -0+#"; char *p1, *p2; /* * Remove all conversion flags; '-' is the only one valid * with %s, and it's not useful here. */ pr->flags = F_BPAD; pr->cchar[0] = 's'; pr->cchar[1] = '\0'; for (p1 = pr->fmt; *p1 != '%'; ++p1); for (p2 = ++p1; *p1 && strchr(spec, *p1); ++p1); while ((*p2++ = *p1++)); } static char **_argv; u_char * get(void) { static int ateof = 1; static u_char *curp, *savp; int n; int need, nread; int valid_save = 0; u_char *tmpp; if (!curp) { if ((curp = calloc(1, blocksize)) == NULL) err(1, NULL); if ((savp = calloc(1, blocksize)) == NULL) err(1, NULL); } else { tmpp = curp; curp = savp; savp = tmpp; address += blocksize; valid_save = 1; } for (need = blocksize, nread = 0;;) { /* * if read the right number of bytes, or at EOF for one file, * and no other files are available, zero-pad the rest of the * block and set the end flag. */ if (!length || (ateof && !next((char **)NULL))) { if (odmode && skip > 0) errx(1, "cannot skip past end of input"); if (need == blocksize) return((u_char *)NULL); /* * XXX bcmp() is not quite right in the presence * of multibyte characters. */ if (need == 0 && vflag != ALL && valid_save && bcmp(curp, savp, nread) == 0) { if (vflag != DUP) { (void)printf("*\n"); (void)fflush(stdout); } return((u_char *)NULL); } bzero((char *)curp + nread, need); eaddress = address + nread; return(curp); } n = fread((char *)curp + nread, sizeof(u_char), length == -1 ? need : MIN(length, need), stdin); if (!n) { if (ferror(stdin)) warn("%s", _argv[-1]); ateof = 1; continue; } ateof = 0; if (length != -1) length -= n; if (!(need -= n)) { /* * XXX bcmp() is not quite right in the presence * of multibyte characters. */ if (vflag == ALL || vflag == FIRST || valid_save == 0 || bcmp(curp, savp, blocksize) != 0) { if (vflag == DUP || vflag == FIRST) vflag = WAIT; return(curp); } if (vflag == WAIT) { (void)printf("*\n"); (void)fflush(stdout); } vflag = DUP; address += blocksize; need = blocksize; nread = 0; } else nread += n; } } size_t peek(u_char *buf, size_t nbytes) { size_t n, nread; int c; if (length != -1 && nbytes > (unsigned int)length) nbytes = length; nread = 0; while (nread < nbytes && (c = getchar()) != EOF) { *buf++ = c; nread++; } n = nread; while (n-- > 0) { c = *--buf; ungetc(c, stdin); } return (nread); } int next(char **argv) { static int done; int statok; if (argv) { _argv = argv; return(1); } for (;;) { if (*_argv) { done = 1; if (!(freopen(*_argv, "r", stdin))) { warn("%s", *_argv); exitval = 1; ++_argv; continue; } statok = 1; } else { if (done++) return(0); statok = 0; } if (caph_limit_stream(fileno(stdin), CAPH_READ) < 0) err(1, "unable to restrict %s", statok ? *_argv : "stdin"); /* * We've opened our last input file; enter capsicum sandbox. */ if (statok == 0 || *(_argv + 1) == NULL) { if (caph_enter() < 0) err(1, "unable to enter capability mode"); } if (skip) doskip(statok ? *_argv : "stdin", statok); if (*_argv) ++_argv; if (!skip) return(1); } /* NOTREACHED */ } void doskip(const char *fname, int statok) { int type; struct stat sb; if (statok) { if (fstat(fileno(stdin), &sb)) err(1, "%s", fname); if (S_ISREG(sb.st_mode) && skip > sb.st_size) { address += sb.st_size; skip -= sb.st_size; return; } } if (!statok || S_ISFIFO(sb.st_mode) || S_ISSOCK(sb.st_mode)) { noseek(); return; } if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) { if (ioctl(fileno(stdin), FIODTYPE, &type)) err(1, "%s", fname); /* * Most tape drives don't support seeking, * yet fseek() would succeed. */ if (type & D_TAPE) { noseek(); return; } } if (fseeko(stdin, skip, SEEK_SET)) { noseek(); return; } address += skip; skip = 0; } static void noseek(void) { int count; for (count = 0; count < skip; ++count) if (getchar() == EOF) break; address += count; skip -= count; } diff --git a/usr.bin/hexdump/hexsyntax.c b/usr.bin/hexdump/hexsyntax.c index 649d3028c603..de6b384cdb73 100644 --- a/usr.bin/hexdump/hexsyntax.c +++ b/usr.bin/hexdump/hexsyntax.c @@ -1,137 +1,134 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 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. 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 -#endif /* not lint */ -#include #include #include #include #include #include #include #include "hexdump.h" off_t skip; /* bytes to skip */ void newsyntax(int argc, char ***argvp) { int ch; char *p, **argv; argv = *argvp; if ((p = strrchr(argv[0], 'h')) != NULL && strcmp(p, "hd") == 0) { /* "Canonical" format, implies -C. */ add("\"%08.8_Ax\n\""); add("\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" "); add("\" |\" 16/1 \"%_p\" \"|\\n\""); } while ((ch = getopt(argc, argv, "bcCde:f:n:os:vx")) != -1) switch (ch) { case 'b': add("\"%07.7_Ax\n\""); add("\"%07.7_ax \" 16/1 \"%03o \" \"\\n\""); break; case 'c': add("\"%07.7_Ax\n\""); add("\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\""); break; case 'C': add("\"%08.8_Ax\n\""); add("\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" "); add("\" |\" 16/1 \"%_p\" \"|\\n\""); break; case 'd': add("\"%07.7_Ax\n\""); add("\"%07.7_ax \" 8/2 \" %05u \" \"\\n\""); break; case 'e': add(optarg); break; case 'f': addfile(optarg); break; case 'n': if ((length = atoi(optarg)) < 0) errx(1, "%s: bad length value", optarg); break; case 'o': add("\"%07.7_Ax\n\""); add("\"%07.7_ax \" 8/2 \" %06o \" \"\\n\""); break; case 's': if ((skip = strtoll(optarg, &p, 0)) < 0) errx(1, "%s: bad skip value", optarg); switch(*p) { case 'b': skip *= 512; break; case 'k': skip *= 1024; break; case 'm': skip *= 1048576; break; } break; case 'v': vflag = ALL; break; case 'x': add("\"%07.7_Ax\n\""); add("\"%07.7_ax \" 8/2 \" %04x \" \"\\n\""); break; case '?': usage(); } if (!fshead) { add("\"%07.7_Ax\n\""); add("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\""); } *argvp += optind; } void usage(void) { (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", "usage: hexdump [-bcCdovx] [-e fmt] [-f fmt_file] [-n length]", " [-s skip] [file ...]", " hd [-bcdovx] [-e fmt] [-f fmt_file] [-n length]", " [-s skip] [file ...]"); exit(1); } diff --git a/usr.bin/hexdump/odsyntax.c b/usr.bin/hexdump/odsyntax.c index 1ea300089ec8..6c7ca55c3232 100644 --- a/usr.bin/hexdump/odsyntax.c +++ b/usr.bin/hexdump/odsyntax.c @@ -1,435 +1,432 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 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. 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 -#endif /* not lint */ -#include #include #include #include #include #include #include #include #include #include #include "hexdump.h" #define PADDING " " int odmode; static void odadd(const char *); static void odformat(const char *); static const char *odformatfp(char, const char *); static const char *odformatint(char, const char *); static void odoffset(int, char ***); static void odusage(void); void oldsyntax(int argc, char ***argvp) { static char empty[] = "", padding[] = PADDING; int ch; char **argv, *end; /* Add initial (default) address format. -A may change it later. */ #define TYPE_OFFSET 7 add("\"%07.7_Ao\n\""); add("\"%07.7_ao \""); odmode = 1; argv = *argvp; while ((ch = getopt(argc, argv, "A:aBbcDdeFfHhIij:LlN:Oost:vXx")) != -1) switch (ch) { case 'A': switch (*optarg) { case 'd': case 'o': case 'x': fshead->nextfu->fmt[TYPE_OFFSET] = *optarg; fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = *optarg; break; case 'n': fshead->nextfu->fmt = empty; fshead->nextfs->nextfu->fmt = padding; break; default: errx(1, "%s: invalid address base", optarg); } break; case 'a': odformat("a"); break; case 'B': case 'o': odformat("o2"); break; case 'b': odformat("o1"); break; case 'c': odformat("c"); break; case 'd': odformat("u2"); break; case 'D': odformat("u4"); break; case 'e': /* undocumented in od */ case 'F': odformat("fD"); break; case 'f': odformat("fF"); break; case 'H': case 'X': odformat("x4"); break; case 'h': case 'x': odformat("x2"); break; case 'I': case 'L': case 'l': odformat("dL"); break; case 'i': odformat("dI"); break; case 'j': errno = 0; skip = strtoll(optarg, &end, 0); if (*end == 'b') skip *= 512; else if (*end == 'k') skip *= 1024; else if (*end == 'm') skip *= 1048576L; if (errno != 0 || skip < 0 || strlen(end) > 1) errx(1, "%s: invalid skip amount", optarg); break; case 'N': if ((length = atoi(optarg)) <= 0) errx(1, "%s: invalid length", optarg); break; case 'O': odformat("o4"); break; case 's': odformat("d2"); break; case 't': odformat(optarg); break; case 'v': vflag = ALL; break; case '?': default: odusage(); } if (fshead->nextfs->nextfs == NULL) odformat("oS"); argc -= optind; *argvp += optind; if (argc) odoffset(argc, argvp); } static void odusage(void) { fprintf(stderr, "usage: od [-aBbcDdeFfHhIiLlOosvXx] [-A base] [-j skip] [-N length] [-t type]\n"); fprintf(stderr, " [[+]offset[.][Bb]] [file ...]\n"); exit(1); } static void odoffset(int argc, char ***argvp) { char *p, *num, *end; int base; /* * The offset syntax of od(1) was genuinely bizarre. First, if * it started with a plus it had to be an offset. Otherwise, if * there were at least two arguments, a number or lower-case 'x' * followed by a number makes it an offset. By default it was * octal; if it started with 'x' or '0x' it was hex. If it ended * in a '.', it was decimal. If a 'b' or 'B' was appended, it * multiplied the number by 512 or 1024 byte units. There was * no way to assign a block count to a hex offset. * * We assume it's a file if the offset is bad. */ p = argc == 1 ? (*argvp)[0] : (*argvp)[1]; if (*p != '+' && (argc < 2 || (!isdigit(p[0]) && (p[0] != 'x' || !isxdigit(p[1]))))) return; base = 0; /* * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and * set base. */ if (p[0] == '+') ++p; if (p[0] == 'x' && isxdigit(p[1])) { ++p; base = 16; } else if (p[0] == '0' && p[1] == 'x') { p += 2; base = 16; } /* skip over the number */ if (base == 16) for (num = p; isxdigit(*p); ++p); else for (num = p; isdigit(*p); ++p); /* check for no number */ if (num == p) return; /* if terminates with a '.', base is decimal */ if (*p == '.') { if (base) return; base = 10; } skip = strtoll(num, &end, base ? base : 8); /* if end isn't the same as p, we got a non-octal digit */ if (end != p) { skip = 0; return; } if (*p) { if (*p == 'B') { skip *= 1024; ++p; } else if (*p == 'b') { skip *= 512; ++p; } } if (*p) { skip = 0; return; } /* * If the offset uses a non-octal base, the base of the offset * is changed as well. This isn't pretty, but it's easy. */ if (base == 16) { fshead->nextfu->fmt[TYPE_OFFSET] = 'x'; fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'x'; } else if (base == 10) { fshead->nextfu->fmt[TYPE_OFFSET] = 'd'; fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'd'; } /* Terminate file list. */ (*argvp)[1] = NULL; } static void odformat(const char *fmt) { char fchar; while (*fmt != '\0') { switch ((fchar = *fmt++)) { case 'a': odadd("16/1 \"%3_u \" \"\\n\""); break; case 'c': odadd("16/1 \"%3_c \" \"\\n\""); break; case 'o': case 'u': case 'd': case 'x': fmt = odformatint(fchar, fmt); break; case 'f': fmt = odformatfp(fchar, fmt); break; default: errx(1, "%c: unrecognised format character", fchar); } } } static const char * odformatfp(char fchar __unused, const char *fmt) { size_t isize; int digits; char *end, *hdfmt; isize = sizeof(double); switch (*fmt) { case 'F': isize = sizeof(float); fmt++; break; case 'D': isize = sizeof(double); fmt++; break; case 'L': isize = sizeof(long double); fmt++; break; default: if (isdigit((unsigned char)*fmt)) { errno = 0; isize = (size_t)strtoul(fmt, &end, 10); if (errno != 0 || isize == 0) errx(1, "%s: invalid size", fmt); fmt = (const char *)end; } } switch (isize) { case sizeof(float): digits = FLT_DIG; break; case sizeof(double): digits = DBL_DIG; break; default: if (isize == sizeof(long double)) digits = LDBL_DIG; else errx(1, "unsupported floating point size %lu", (u_long)isize); } asprintf(&hdfmt, "%lu/%lu \" %%%d.%de \" \"\\n\"", 16UL / (u_long)isize, (u_long)isize, digits + 8, digits); if (hdfmt == NULL) err(1, NULL); odadd(hdfmt); free(hdfmt); return (fmt); } static const char * odformatint(char fchar, const char *fmt) { unsigned long long n; size_t isize; int digits; char *end, *hdfmt; isize = sizeof(int); switch (*fmt) { case 'C': isize = sizeof(char); fmt++; break; case 'I': isize = sizeof(int); fmt++; break; case 'L': isize = sizeof(long); fmt++; break; case 'S': isize = sizeof(short); fmt++; break; default: if (isdigit((unsigned char)*fmt)) { errno = 0; isize = (size_t)strtoul(fmt, &end, 10); if (errno != 0 || isize == 0) errx(1, "%s: invalid size", fmt); if (isize != sizeof(char) && isize != sizeof(short) && isize != sizeof(int) && isize != sizeof(long)) errx(1, "unsupported int size %lu", (u_long)isize); fmt = (const char *)end; } } /* * Calculate the maximum number of digits we need to * fit the number. Overestimate for decimal with log * base 8. We need one extra space for signed numbers * to store the sign. */ n = (1ULL << (8 * isize)) - 1; digits = 0; while (n != 0) { digits++; n >>= (fchar == 'x') ? 4 : 3; } if (fchar == 'd') digits++; asprintf(&hdfmt, "%lu/%lu \"%*s%%%s%d%c\" \"\\n\"", 16UL / (u_long)isize, (u_long)isize, (int)(4 * isize - digits), "", (fchar == 'd' || fchar == 'u') ? "" : "0", digits, fchar); if (hdfmt == NULL) err(1, NULL); odadd(hdfmt); free(hdfmt); return (fmt); } static void odadd(const char *fmt) { static int needpad; if (needpad) add("\""PADDING"\""); add(fmt); needpad = 1; } diff --git a/usr.bin/hexdump/parse.c b/usr.bin/hexdump/parse.c index 9d0ab1feaa7e..0f61763bd7bf 100644 --- a/usr.bin/hexdump/parse.c +++ b/usr.bin/hexdump/parse.c @@ -1,527 +1,524 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include #include #include #include #include #include #include #include "hexdump.h" FU *endfu; /* format at end-of-data */ void addfile(const char *name) { unsigned char *p; FILE *fp; int ch; char buf[2048 + 1]; if ((fp = fopen(name, "r")) == NULL) err(1, "%s", name); while (fgets(buf, sizeof(buf), fp)) { if (!(p = strchr(buf, '\n'))) { warnx("line too long"); while ((ch = getchar()) != '\n' && ch != EOF); continue; } *p = '\0'; for (p = buf; *p && isspace(*p); ++p); if (!*p || *p == '#') continue; add(p); } (void)fclose(fp); } void add(const char *fmt) { unsigned const char *p, *savep; static FS **nextfs; FS *tfs; FU *tfu, **nextfu; /* start new linked list of format units */ if ((tfs = calloc(1, sizeof(FS))) == NULL) err(1, NULL); if (!fshead) fshead = tfs; else *nextfs = tfs; nextfs = &tfs->nextfs; nextfu = &tfs->nextfu; /* take the format string and break it up into format units */ for (p = fmt;;) { /* skip leading white space */ for (; isspace(*p); ++p); if (!*p) break; /* allocate a new format unit and link it in */ if ((tfu = calloc(1, sizeof(FU))) == NULL) err(1, NULL); *nextfu = tfu; nextfu = &tfu->nextfu; tfu->reps = 1; /* if leading digit, repetition count */ if (isdigit(*p)) { for (savep = p; isdigit(*p); ++p); if (!isspace(*p) && *p != '/') badfmt(fmt); /* may overwrite either white space or slash */ tfu->reps = atoi(savep); tfu->flags = F_SETREP; /* skip trailing white space */ for (++p; isspace(*p); ++p); } /* skip slash and trailing white space */ if (*p == '/') while (isspace(*++p)); /* byte count */ if (isdigit(*p)) { for (savep = p; isdigit(*p); ++p); if (!isspace(*p)) badfmt(fmt); tfu->bcnt = atoi(savep); /* skip trailing white space */ for (++p; isspace(*p); ++p); } /* format */ if (*p != '"') badfmt(fmt); for (savep = ++p; *p != '"';) if (*p++ == 0) badfmt(fmt); if (!(tfu->fmt = malloc(p - savep + 1))) err(1, NULL); (void) strlcpy(tfu->fmt, savep, p - savep + 1); escape(tfu->fmt); p++; } } static const char *spec = ".#-+ 0123456789"; int size(FS *fs) { FU *fu; int bcnt, cursize; unsigned char *fmt; int prec; /* figure out the data block size needed for each format unit */ for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) { if (fu->bcnt) { cursize += fu->bcnt * fu->reps; continue; } for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) { if (*fmt != '%') continue; /* * skip any special chars -- save precision in * case it's a %s format. */ while (*++fmt != 0 && strchr(spec + 1, *fmt) != NULL) ; if (*fmt == 0) badnoconv(); if (*fmt == '.' && isdigit(*++fmt)) { prec = atoi(fmt); while (isdigit(*++fmt)); } switch(*fmt) { case 'c': bcnt += 1; break; case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': bcnt += 4; break; case 'e': case 'E': case 'f': case 'g': case 'G': bcnt += 8; break; case 's': bcnt += prec; break; case '_': switch(*++fmt) { case 'c': case 'p': case 'u': bcnt += 1; break; } } } cursize += bcnt * fu->reps; } return (cursize); } void rewrite(FS *fs) { enum { NOTOKAY, USEBCNT, USEPREC } sokay; PR *pr, **nextpr; FU *fu; unsigned char *p1, *p2, *fmtp; char savech, cs[3]; int nconv, prec; prec = 0; for (fu = fs->nextfu; fu; fu = fu->nextfu) { /* * Break each format unit into print units; each conversion * character gets its own. */ nextpr = &fu->nextpr; for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { if ((pr = calloc(1, sizeof(PR))) == NULL) err(1, NULL); *nextpr = pr; /* Skip preceding text and up to the next % sign. */ for (p1 = fmtp; *p1 && *p1 != '%'; ++p1); /* Only text in the string. */ if (!*p1) { pr->fmt = fmtp; pr->flags = F_TEXT; break; } /* * Get precision for %s -- if have a byte count, don't * need it. */ if (fu->bcnt) { sokay = USEBCNT; /* Skip to conversion character. */ while (*++p1 != 0 && strchr(spec, *p1) != NULL) ; if (*p1 == 0) badnoconv(); } else { /* Skip any special chars, field width. */ while (*++p1 != 0 && strchr(spec + 1, *p1) != NULL) ; if (*p1 == 0) badnoconv(); if (*p1 == '.' && isdigit(*++p1)) { sokay = USEPREC; prec = atoi(p1); while (isdigit(*++p1)); } else sokay = NOTOKAY; } p2 = *p1 ? p1 + 1 : p1; /* Set end pointer -- make sure * that it's non-NUL/-NULL first * though. */ cs[0] = *p1; /* Set conversion string. */ cs[1] = '\0'; /* * Figure out the byte count for each conversion; * rewrite the format as necessary, set up blank- * padding for end of data. */ switch(cs[0]) { case 'c': pr->flags = F_CHAR; switch(fu->bcnt) { case 0: case 1: pr->bcnt = 1; break; default: p1[1] = '\0'; badcnt(p1); } break; case 'd': case 'i': pr->flags = F_INT; goto isint; case 'o': case 'u': case 'x': case 'X': pr->flags = F_UINT; isint: cs[2] = '\0'; cs[1] = cs[0]; cs[0] = 'q'; switch(fu->bcnt) { case 0: case 4: pr->bcnt = 4; break; case 1: pr->bcnt = 1; break; case 2: pr->bcnt = 2; break; case 8: pr->bcnt = 8; break; default: p1[1] = '\0'; badcnt(p1); } break; case 'e': case 'E': case 'f': case 'g': case 'G': pr->flags = F_DBL; switch(fu->bcnt) { case 0: case 8: pr->bcnt = 8; break; case 4: pr->bcnt = 4; break; default: if (fu->bcnt == sizeof(long double)) { cs[2] = '\0'; cs[1] = cs[0]; cs[0] = 'L'; pr->bcnt = sizeof(long double); } else { p1[1] = '\0'; badcnt(p1); } } break; case 's': pr->flags = F_STR; switch(sokay) { case NOTOKAY: badsfmt(); case USEBCNT: pr->bcnt = fu->bcnt; break; case USEPREC: pr->bcnt = prec; break; } break; case '_': ++p2; switch(p1[1]) { case 'A': endfu = fu; fu->flags |= F_IGNORE; /* FALLTHROUGH */ case 'a': pr->flags = F_ADDRESS; ++p2; switch(p1[2]) { case 'd': case 'o': case'x': cs[0] = 'q'; cs[1] = p1[2]; cs[2] = '\0'; break; default: p1[3] = '\0'; badconv(p1); } break; case 'c': pr->flags = F_C; /* cs[0] = 'c'; set in conv_c */ goto isint2; case 'p': pr->flags = F_P; cs[0] = 'c'; goto isint2; case 'u': pr->flags = F_U; /* cs[0] = 'c'; set in conv_u */ isint2: switch(fu->bcnt) { case 0: case 1: pr->bcnt = 1; break; default: p1[2] = '\0'; badcnt(p1); } break; default: p1[2] = '\0'; badconv(p1); } break; default: p1[1] = '\0'; badconv(p1); } /* * Copy to PR format string, set conversion character * pointer, update original. */ savech = *p2; p1[0] = '\0'; if (asprintf(&pr->fmt, "%s%s", fmtp, cs) == -1) err(1, NULL); *p2 = savech; pr->cchar = pr->fmt + (p1 - fmtp); fmtp = p2; /* Only one conversion character if byte count. */ if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++) errx(1, "byte count with multiple conversion characters"); } /* * If format unit byte count not specified, figure it out * so can adjust rep count later. */ if (!fu->bcnt) for (pr = fu->nextpr; pr; pr = pr->nextpr) fu->bcnt += pr->bcnt; } /* * If the format string interprets any data at all, and it's * not the same as the blocksize, and its last format unit * interprets any data at all, and has no iteration count, * repeat it as necessary. * * If, rep count is greater than 1, no trailing whitespace * gets output from the last iteration of the format unit. */ for (fu = fs->nextfu; fu; fu = fu->nextfu) { if (!fu->nextfu && fs->bcnt < blocksize && !(fu->flags&F_SETREP) && fu->bcnt) fu->reps += (blocksize - fs->bcnt) / fu->bcnt; if (fu->reps > 1) { for (pr = fu->nextpr;; pr = pr->nextpr) if (!pr->nextpr) break; for (p1 = pr->fmt, p2 = NULL; *p1; ++p1) p2 = isspace(*p1) ? p1 : NULL; if (p2) pr->nospace = p2; } } #ifdef DEBUG for (fu = fs->nextfu; fu; fu = fu->nextfu) { (void)printf("fmt:"); for (pr = fu->nextpr; pr; pr = pr->nextpr) (void)printf(" {%s}", pr->fmt); (void)printf("\n"); } #endif } void escape(char *p1) { char *p2; /* alphabetic escape sequences have to be done in place */ for (p2 = p1;; p1++, p2++) { if (*p1 == '\\') { p1++; switch(*p1) { case '\0': *p2 = '\\'; *++p2 = '\0'; return; case 'a': /* *p2 = '\a'; */ *p2 = '\007'; break; case 'b': *p2 = '\b'; break; case 'f': *p2 = '\f'; break; case 'n': *p2 = '\n'; break; case 'r': *p2 = '\r'; break; case 't': *p2 = '\t'; break; case 'v': *p2 = '\v'; break; default: *p2 = *p1; break; } } else { *p2 = *p1; if (*p1 == '\0') return; } } } void badcnt(const char *s) { errx(1, "%s: bad byte count", s); } void badsfmt(void) { errx(1, "%%s: requires a precision or a byte count"); } void badfmt(const char *fmt) { errx(1, "\"%s\": bad format", fmt); } void badconv(const char *ch) { errx(1, "%%%s: bad conversion character", ch); } void badnoconv(void) { errx(1, "missing conversion character"); } diff --git a/usr.bin/iconv/iconv.c b/usr.bin/iconv/iconv.c index 54a464de273a..7d1b3c49eee6 100644 --- a/usr.bin/iconv/iconv.c +++ b/usr.bin/iconv/iconv.c @@ -1,249 +1,248 @@ /* $NetBSD: iconv.c,v 1.16 2009/02/20 15:28:21 yamt Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c)2003 Citrus Project, * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include static int do_conv(FILE *, iconv_t, bool, bool); static int do_list(unsigned int, const char * const *, void *); static void usage(void) __dead2; static const struct option long_options[] = { {"from-code", required_argument, NULL, 'f'}, {"list", no_argument, NULL, 'l'}, {"silent", no_argument, NULL, 's'}, {"to-code", required_argument, NULL, 't'}, {NULL, no_argument, NULL, 0} }; static void usage(void) { (void)fprintf(stderr, "Usage:\t%1$s [-cs] -f -t [file ...]\n" "\t%1$s -f [-cs] [-t ] [file ...]\n" "\t%1$s -t [-cs] [-f ] [file ...]\n" "\t%1$s -l\n", getprogname()); exit(1); } #define INBUFSIZE 1024 #define OUTBUFSIZE (INBUFSIZE * 2) static int do_conv(FILE *fp, iconv_t cd, bool silent, bool hide_invalid) { char inbuf[INBUFSIZE], outbuf[OUTBUFSIZE], *in, *out; unsigned long long invalids; size_t inbytes, outbytes, ret; /* * Don't touch ICONV_SET_DISCARD_ILSEQ if -c wasn't specified. It may * be that the user has specified //IGNORE in the -t specification, and * we don't want to clobber that. */ if (hide_invalid) { int arg = (int)hide_invalid; if (iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, (void *)&arg) == -1) err(EXIT_FAILURE, "iconvctl(DISCARD_ILSEQ, %d)", arg); } invalids = 0; while ((inbytes = fread(inbuf, 1, INBUFSIZE, fp)) > 0) { in = inbuf; while (inbytes > 0) { size_t inval; out = outbuf; outbytes = OUTBUFSIZE; ret = __iconv(cd, &in, &inbytes, &out, &outbytes, 0, &inval); invalids += inval; if (outbytes < OUTBUFSIZE) (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, stdout); if (ret == (size_t)-1 && errno != E2BIG) { if (errno != EINVAL || in == inbuf) err(EXIT_FAILURE, "iconv()"); /* incomplete input character */ (void)memmove(inbuf, in, inbytes); ret = fread(inbuf + inbytes, 1, INBUFSIZE - inbytes, fp); if (ret == 0) { fflush(stdout); if (feof(fp)) errx(EXIT_FAILURE, "unexpected end of file; " "the last character is " "incomplete."); else err(EXIT_FAILURE, "fread()"); } in = inbuf; inbytes += ret; } } } /* reset the shift state of the output buffer */ outbytes = OUTBUFSIZE; out = outbuf; ret = iconv(cd, NULL, NULL, &out, &outbytes); if (ret == (size_t)-1) err(EXIT_FAILURE, "iconv()"); if (outbytes < OUTBUFSIZE) (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, stdout); if (invalids > 0 && !silent) warnx("warning: invalid characters: %llu", invalids); return (invalids > 0); } static int do_list(unsigned int n, const char * const *list, void *data __unused) { unsigned int i; for(i = 0; i < n; i++) { printf("%s", list[i]); if (i < n - 1) printf(" "); } printf("\n"); return (1); } int main(int argc, char **argv) { iconv_t cd; FILE *fp; const char *opt_f, *opt_t; int ch, i, res; bool opt_c = false, opt_s = false; opt_f = opt_t = ""; setlocale(LC_ALL, ""); setprogname(argv[0]); while ((ch = getopt_long(argc, argv, "csLlf:t:", long_options, NULL)) != -1) { switch (ch) { case 'c': opt_c = true; break; case 's': opt_s = true; break; case 'l': /* list */ if (opt_s || opt_c || strcmp(opt_f, "") != 0 || strcmp(opt_t, "") != 0) { warnx("-l is not allowed with other flags."); usage(); } iconvlist(do_list, NULL); return (EXIT_SUCCESS); case 'f': /* from */ if (optarg != NULL) opt_f = optarg; break; case 't': /* to */ if (optarg != NULL) opt_t = optarg; break; default: usage(); } } argc -= optind; argv += optind; if ((strcmp(opt_f, "") == 0) && (strcmp(opt_t, "") == 0)) usage(); if (caph_limit_stdio() < 0) err(EXIT_FAILURE, "capsicum"); /* * Cache NLS data, for strerror, for err(3), before entering capability * mode. */ caph_cache_catpages(); /* * Cache iconv conversion handle before entering sandbox. */ cd = iconv_open(opt_t, opt_f); if (cd == (iconv_t)-1) err(EXIT_FAILURE, "iconv_open(%s, %s)", opt_t, opt_f); if (argc == 0) { if (caph_enter() < 0) err(EXIT_FAILURE, "unable to enter capability mode"); res = do_conv(stdin, cd, opt_s, opt_c); } else { res = 0; for (i = 0; i < argc; i++) { fp = (strcmp(argv[i], "-") != 0) ? fopen(argv[i], "r") : stdin; if (fp == NULL) err(EXIT_FAILURE, "Cannot open `%s'", argv[i]); /* Enter Capsicum sandbox for final input file. */ if (i + 1 == argc && caph_enter() < 0) err(EXIT_FAILURE, "unable to enter capability mode"); res |= do_conv(fp, cd, opt_s, opt_c); (void)fclose(fp); /* Reset iconv descriptor state. */ (void)iconv(cd, NULL, NULL, NULL, NULL); } } iconv_close(cd); return (res == 0 ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/usr.bin/ident/ident.c b/usr.bin/ident/ident.c index 84ac402f74d0..96190723dbf7 100644 --- a/usr.bin/ident/ident.c +++ b/usr.bin/ident/ident.c @@ -1,287 +1,286 @@ /*- * Copyright (c) 2015-2021 Baptiste Daroussin * Copyright (c) 2015 Xin LI * * 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 * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef enum { /* state condition to transit to next state */ INIT, /* '$' */ DELIM_SEEN, /* letter */ KEYWORD, /* punctuation mark */ PUNC_SEEN, /* ':' -> _SVN; space -> TEXT */ PUNC_SEEN_SVN, /* space */ TEXT } analyzer_states; static int scan(FILE *fp, const char *name, bool quiet) { int c; bool hasid = false; bool subversion = false; analyzer_states state = INIT; FILE* buffp; char *buf; size_t sz; locale_t l; l = newlocale(LC_ALL_MASK, "C", NULL); sz = 0; buf = NULL; buffp = open_memstream(&buf, &sz); if (buffp == NULL) err(EXIT_FAILURE, "open_memstream()"); if (name != NULL) printf("%s:\n", name); while ((c = fgetc(fp)) != EOF) { switch (state) { case INIT: if (c == '$') { /* Transit to DELIM_SEEN if we see $ */ state = DELIM_SEEN; } else { /* Otherwise, stay in INIT state */ continue; } break; case DELIM_SEEN: if (isalpha_l(c, l)) { /* Transit to KEYWORD if we see letter */ if (buf != NULL) memset(buf, 0, sz); rewind(buffp); fputc('$', buffp); fputc(c, buffp); state = KEYWORD; continue; } else if (c == '$') { /* Or, stay in DELIM_SEEN if more $ */ continue; } else { /* Otherwise, transit back to INIT */ state = INIT; } break; case KEYWORD: fputc(c, buffp); if (isalpha_l(c, l)) { /* * Stay in KEYWORD if additional letter is seen */ continue; } else if (c == ':') { /* * See ':' for the first time, transit to * PUNC_SEEN. */ state = PUNC_SEEN; subversion = false; } else if (c == '$') { /* * Incomplete ident. Go back to DELIM_SEEN * state because we see a '$' which could be * the beginning of a keyword. */ state = DELIM_SEEN; } else { /* * Go back to INIT state otherwise. */ state = INIT; } break; case PUNC_SEEN: case PUNC_SEEN_SVN: fputc(c, buffp); switch (c) { case ':': /* * If we see '::' (seen : in PUNC_SEEN), * activate subversion treatment and transit * to PUNC_SEEN_SVN state. * * If more than two :'s were seen, the ident * is invalid and we would therefore go back * to INIT state. */ if (state == PUNC_SEEN) { state = PUNC_SEEN_SVN; subversion = true; } else { state = INIT; } break; case ' ': /* * A space after ':' or '::' indicates we are at the * last component of potential ident. */ state = TEXT; break; default: /* All other characters are invalid */ state = INIT; break; } break; case TEXT: fputc(c, buffp); if (iscntrl_l(c, l)) { /* Control characters are not allowed in this state */ state = INIT; } else if (c == '$') { fflush(buffp); /* * valid ident should end with a space. * * subversion extension uses '#' to indicate that * the keyword expansion have exceeded the fixed * width, so it is also permitted if we are in * subversion mode. No length check is enforced * because GNU RCS ident(1) does not do it either. */ c = buf[strlen(buf) -2 ]; if (c == ' ' || (subversion && c == '#')) { printf(" %s\n", buf); hasid = true; } state = INIT; } /* Other characters: stay in the state */ break; } } fclose(buffp); free(buf); freelocale(l); if (!hasid) { if (!quiet) fprintf(stderr, "%s warning: no id keywords in %s\n", getprogname(), name ? name : "standard input"); return (EXIT_FAILURE); } return (EXIT_SUCCESS); } int main(int argc, char **argv) { bool quiet = false; int ch, i, *fds, fd; int ret = EXIT_SUCCESS; size_t nfds; FILE *fp; while ((ch = getopt(argc, argv, "qV")) != -1) { switch (ch) { case 'q': quiet = true; break; case 'V': /* Do nothing, compat with GNU rcs's ident */ return (EXIT_SUCCESS); default: errx(EXIT_FAILURE, "usage: %s [-q] [-V] [file...]", getprogname()); } } argc -= optind; argv += optind; if (caph_limit_stdio() < 0) err(EXIT_FAILURE, "unable to limit stdio"); if (argc == 0) { nfds = 1; fds = malloc(sizeof(*fds)); if (fds == NULL) err(EXIT_FAILURE, "unable to allocate fds array"); fds[0] = STDIN_FILENO; } else { nfds = argc; fds = malloc(sizeof(*fds) * nfds); if (fds == NULL) err(EXIT_FAILURE, "unable to allocate fds array"); for (i = 0; i < argc; i++) { fds[i] = fd = open(argv[i], O_RDONLY); if (fd < 0) { warn("%s", argv[i]); ret = EXIT_FAILURE; continue; } if (caph_limit_stream(fd, CAPH_READ) < 0) err(EXIT_FAILURE, "unable to limit fcntls/rights for %s", argv[i]); } } /* Enter Capsicum sandbox. */ if (caph_enter() < 0) err(EXIT_FAILURE, "unable to enter capability mode"); for (i = 0; i < (int)nfds; i++) { if (fds[i] < 0) continue; fp = fdopen(fds[i], "r"); if (fp == NULL) { warn("%s", argv[i]); ret = EXIT_FAILURE; continue; } if (scan(fp, argc == 0 ? NULL : argv[i], quiet) != EXIT_SUCCESS) ret = EXIT_FAILURE; fclose(fp); } return (ret); } diff --git a/usr.bin/indent/args.c b/usr.bin/indent/args.c index 27dc8d0b5fca..fe03557169c2 100644 --- a/usr.bin/indent/args.c +++ b/usr.bin/indent/args.c @@ -1,351 +1,348 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1985 Sun Microsystems, Inc. * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * 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 -#endif - #include /* * Argument scanning and profile reading code. Default parameters are set * here as well. */ #include #include #include #include #include #include #include "indent_globs.h" #include "indent.h" #define INDENT_VERSION "2.0" /* profile types */ #define PRO_SPECIAL 1 /* special case */ #define PRO_BOOL 2 /* boolean */ #define PRO_INT 3 /* integer */ /* profile specials for booleans */ #define ON 1 /* turn it on */ #define OFF 0 /* turn it off */ /* profile specials for specials */ #define IGN 1 /* ignore it */ #define CLI 2 /* case label indent (float) */ #define STDIN 3 /* use stdin */ #define KEY 4 /* type (keyword) */ static void scan_profile(FILE *); #define KEY_FILE 5 /* only used for args */ #define VERSION 6 /* only used for args */ const char *option_source = "?"; void add_typedefs_from_file(const char *str); /* * N.B.: because of the way the table here is scanned, options whose names are * substrings of other options must occur later; that is, with -lp vs -l, -lp * must be first. Also, while (most) booleans occur more than once, the last * default value is the one actually assigned. */ struct pro { const char *p_name; /* name, e.g. -bl, -cli */ int p_type; /* type (int, bool, special) */ int p_default; /* the default value (if int) */ int p_special; /* depends on type */ int *p_obj; /* the associated variable */ } pro[] = { {"T", PRO_SPECIAL, 0, KEY, 0}, {"U", PRO_SPECIAL, 0, KEY_FILE, 0}, {"-version", PRO_SPECIAL, 0, VERSION, 0}, {"P", PRO_SPECIAL, 0, IGN, 0}, {"bacc", PRO_BOOL, false, ON, &opt.blanklines_around_conditional_compilation}, {"badp", PRO_BOOL, false, ON, &opt.blanklines_after_declarations_at_proctop}, {"bad", PRO_BOOL, false, ON, &opt.blanklines_after_declarations}, {"bap", PRO_BOOL, false, ON, &opt.blanklines_after_procs}, {"bbb", PRO_BOOL, false, ON, &opt.blanklines_before_blockcomments}, {"bc", PRO_BOOL, true, OFF, &opt.leave_comma}, {"bl", PRO_BOOL, true, OFF, &opt.btype_2}, {"br", PRO_BOOL, true, ON, &opt.btype_2}, {"bs", PRO_BOOL, false, ON, &opt.Bill_Shannon}, {"cdb", PRO_BOOL, true, ON, &opt.comment_delimiter_on_blankline}, {"cd", PRO_INT, 0, 0, &opt.decl_com_ind}, {"ce", PRO_BOOL, true, ON, &opt.cuddle_else}, {"ci", PRO_INT, 0, 0, &opt.continuation_indent}, {"cli", PRO_SPECIAL, 0, CLI, 0}, {"cs", PRO_BOOL, false, ON, &opt.space_after_cast}, {"c", PRO_INT, 33, 0, &opt.com_ind}, {"di", PRO_INT, 16, 0, &opt.decl_indent}, {"dj", PRO_BOOL, false, ON, &opt.ljust_decl}, {"d", PRO_INT, 0, 0, &opt.unindent_displace}, {"eei", PRO_BOOL, false, ON, &opt.extra_expression_indent}, {"ei", PRO_BOOL, true, ON, &opt.else_if}, {"fbs", PRO_BOOL, true, ON, &opt.function_brace_split}, {"fc1", PRO_BOOL, true, ON, &opt.format_col1_comments}, {"fcb", PRO_BOOL, true, ON, &opt.format_block_comments}, {"ip", PRO_BOOL, true, ON, &opt.indent_parameters}, {"i", PRO_INT, 8, 0, &opt.ind_size}, {"lc", PRO_INT, 0, 0, &opt.block_comment_max_col}, {"ldi", PRO_INT, -1, 0, &opt.local_decl_indent}, {"lpl", PRO_BOOL, false, ON, &opt.lineup_to_parens_always}, {"lp", PRO_BOOL, true, ON, &opt.lineup_to_parens}, {"l", PRO_INT, 78, 0, &opt.max_col}, {"nbacc", PRO_BOOL, false, OFF, &opt.blanklines_around_conditional_compilation}, {"nbadp", PRO_BOOL, false, OFF, &opt.blanklines_after_declarations_at_proctop}, {"nbad", PRO_BOOL, false, OFF, &opt.blanklines_after_declarations}, {"nbap", PRO_BOOL, false, OFF, &opt.blanklines_after_procs}, {"nbbb", PRO_BOOL, false, OFF, &opt.blanklines_before_blockcomments}, {"nbc", PRO_BOOL, true, ON, &opt.leave_comma}, {"nbs", PRO_BOOL, false, OFF, &opt.Bill_Shannon}, {"ncdb", PRO_BOOL, true, OFF, &opt.comment_delimiter_on_blankline}, {"nce", PRO_BOOL, true, OFF, &opt.cuddle_else}, {"ncs", PRO_BOOL, false, OFF, &opt.space_after_cast}, {"ndj", PRO_BOOL, false, OFF, &opt.ljust_decl}, {"neei", PRO_BOOL, false, OFF, &opt.extra_expression_indent}, {"nei", PRO_BOOL, true, OFF, &opt.else_if}, {"nfbs", PRO_BOOL, true, OFF, &opt.function_brace_split}, {"nfc1", PRO_BOOL, true, OFF, &opt.format_col1_comments}, {"nfcb", PRO_BOOL, true, OFF, &opt.format_block_comments}, {"nip", PRO_BOOL, true, OFF, &opt.indent_parameters}, {"nlpl", PRO_BOOL, false, OFF, &opt.lineup_to_parens_always}, {"nlp", PRO_BOOL, true, OFF, &opt.lineup_to_parens}, {"npcs", PRO_BOOL, false, OFF, &opt.proc_calls_space}, {"npro", PRO_SPECIAL, 0, IGN, 0}, {"npsl", PRO_BOOL, true, OFF, &opt.procnames_start_line}, {"nps", PRO_BOOL, false, OFF, &opt.pointer_as_binop}, {"nsc", PRO_BOOL, true, OFF, &opt.star_comment_cont}, {"nsob", PRO_BOOL, false, OFF, &opt.swallow_optional_blanklines}, {"nut", PRO_BOOL, true, OFF, &opt.use_tabs}, {"nv", PRO_BOOL, false, OFF, &opt.verbose}, {"pcs", PRO_BOOL, false, ON, &opt.proc_calls_space}, {"psl", PRO_BOOL, true, ON, &opt.procnames_start_line}, {"ps", PRO_BOOL, false, ON, &opt.pointer_as_binop}, {"sc", PRO_BOOL, true, ON, &opt.star_comment_cont}, {"sob", PRO_BOOL, false, ON, &opt.swallow_optional_blanklines}, {"st", PRO_SPECIAL, 0, STDIN, 0}, {"ta", PRO_BOOL, false, ON, &opt.auto_typedefs}, {"ts", PRO_INT, 8, 0, &opt.tabsize}, {"ut", PRO_BOOL, true, ON, &opt.use_tabs}, {"v", PRO_BOOL, false, ON, &opt.verbose}, /* whew! */ {0, 0, 0, 0, 0} }; /* * set_profile reads $HOME/.indent.pro and ./.indent.pro and handles arguments * given in these files. */ void set_profile(const char *profile_name) { FILE *f; char fname[PATH_MAX]; static char prof[] = ".indent.pro"; if (profile_name == NULL) snprintf(fname, sizeof(fname), "%s/%s", getenv("HOME"), prof); else snprintf(fname, sizeof(fname), "%s", profile_name + 2); if ((f = fopen(option_source = fname, "r")) != NULL) { scan_profile(f); (void) fclose(f); } if ((f = fopen(option_source = prof, "r")) != NULL) { scan_profile(f); (void) fclose(f); } option_source = "Command line"; } static void scan_profile(FILE *f) { int comment, i; char *p; char buf[BUFSIZ]; while (1) { p = buf; comment = 0; while ((i = getc(f)) != EOF) { if (i == '*' && !comment && p > buf && p[-1] == '/') { comment = p - buf; *p++ = i; } else if (i == '/' && comment && p > buf && p[-1] == '*') { p = buf + comment - 1; comment = 0; } else if (isspace((unsigned char)i)) { if (p > buf && !comment) break; } else { *p++ = i; } } if (p != buf) { *p++ = 0; if (opt.verbose) printf("profile: %s\n", buf); set_option(buf); } else if (i == EOF) return; } } static const char * eqin(const char *s1, const char *s2) { while (*s1) { if (*s1++ != *s2++) return (NULL); } return (s2); } /* * Set the defaults. */ void set_defaults(void) { struct pro *p; /* * Because ps.case_indent is a float, we can't initialize it from the * table: */ opt.case_indent = 0.0; /* -cli0.0 */ for (p = pro; p->p_name; p++) if (p->p_type != PRO_SPECIAL) *p->p_obj = p->p_default; } void set_option(char *arg) { struct pro *p; const char *param_start; arg++; /* ignore leading "-" */ for (p = pro; p->p_name; p++) if (*p->p_name == *arg && (param_start = eqin(p->p_name, arg)) != NULL) goto found; errx(1, "%s: unknown parameter \"%s\"", option_source, arg - 1); found: switch (p->p_type) { case PRO_SPECIAL: switch (p->p_special) { case IGN: break; case CLI: if (*param_start == 0) goto need_param; opt.case_indent = atof(param_start); break; case STDIN: if (input == NULL) input = stdin; if (output == NULL) output = stdout; break; case KEY: if (*param_start == 0) goto need_param; add_typename(param_start); break; case KEY_FILE: if (*param_start == 0) goto need_param; add_typedefs_from_file(param_start); break; case VERSION: printf("FreeBSD indent %s\n", INDENT_VERSION); exit(0); default: errx(1, "set_option: internal error: p_special %d", p->p_special); } break; case PRO_BOOL: if (p->p_special == OFF) *p->p_obj = false; else *p->p_obj = true; break; case PRO_INT: if (!isdigit((unsigned char)*param_start)) { need_param: errx(1, "%s: ``%s'' requires a parameter", option_source, p->p_name); } *p->p_obj = atoi(param_start); break; default: errx(1, "set_option: internal error: p_type %d", p->p_type); } } void add_typedefs_from_file(const char *str) { FILE *file; char line[BUFSIZ]; if ((file = fopen(str, "r")) == NULL) { fprintf(stderr, "indent: cannot open file %s\n", str); exit(1); } while ((fgets(line, BUFSIZ, file)) != NULL) { /* Remove trailing whitespace */ line[strcspn(line, " \t\n\r")] = '\0'; add_typename(line); } fclose(file); } diff --git a/usr.bin/indent/indent.c b/usr.bin/indent/indent.c index 5127aa3becaf..4739e861fef9 100644 --- a/usr.bin/indent/indent.c +++ b/usr.bin/indent/indent.c @@ -1,1313 +1,1309 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1985 Sun Microsystems, Inc. * Copyright (c) 1976 Board of Trustees of the University of Illinois. * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include #include "indent_globs.h" #include "indent_codes.h" #include "indent.h" /* Globals */ FILE *input, *output; char *labbuf, *s_lab, *e_lab, *l_lab; char *codebuf, *s_code, *e_code, *l_code; char *combuf, *s_com, *e_com, *l_com; char *tokenbuf, *s_token, *e_token, *l_token; char *in_buffer, *in_buffer_limit; char *buf_ptr, *buf_end; char sc_buf[sc_size]; char *save_com, *sc_end; char *bp_save; char *be_save; struct options opt; int line_no; struct parser_state ps; int ifdef_level; struct parser_state state_stack[5]; struct parser_state match_state[5]; static void bakcopy(void); static void indent_declaration(int, int); const char *in_name = "Standard Input"; /* will always point to name of input * file */ const char *out_name = "Standard Output"; /* will always point to name * of output file */ const char *simple_backup_suffix = ".BAK"; /* Suffix to use for backup * files */ char bakfile[MAXPATHLEN] = ""; int main(int argc, char **argv) { cap_rights_t rights; int dec_ind; /* current indentation for declarations */ int di_stack[20]; /* a stack of structure indentation levels */ int force_nl; /* when true, code must be broken */ int hd_type = 0; /* used to store type of stmt for if (...), * for (...), etc */ int i; /* local loop counter */ int scase; /* set to true when we see a case, so we will * know what to do with the following colon */ int sp_sw; /* when true, we are in the expression of * if(...), while(...), etc. */ int squest; /* when this is positive, we have seen a ? * without the matching : in a ?: * construct */ const char *t_ptr; /* used for copying tokens */ int tabs_to_var; /* true if using tabs to indent to var name */ int type_code; /* the type of token, returned by lexi */ int last_else = 0; /* true iff last keyword was an else */ const char *profile_name = NULL; const char *envval = NULL; struct parser_state transient_state; /* a copy for lookup */ /*-----------------------------------------------*\ | INITIALIZATION | \*-----------------------------------------------*/ found_err = 0; ps.p_stack[0] = stmt; /* this is the parser's stack */ ps.last_nl = true; /* this is true if the last thing scanned was * a newline */ ps.last_token = semicolon; combuf = (char *) malloc(bufsize); if (combuf == NULL) err(1, NULL); labbuf = (char *) malloc(bufsize); if (labbuf == NULL) err(1, NULL); codebuf = (char *) malloc(bufsize); if (codebuf == NULL) err(1, NULL); tokenbuf = (char *) malloc(bufsize); if (tokenbuf == NULL) err(1, NULL); alloc_typenames(); init_constant_tt(); l_com = combuf + bufsize - 5; l_lab = labbuf + bufsize - 5; l_code = codebuf + bufsize - 5; l_token = tokenbuf + bufsize - 5; combuf[0] = codebuf[0] = labbuf[0] = ' '; /* set up code, label, and * comment buffers */ combuf[1] = codebuf[1] = labbuf[1] = '\0'; opt.else_if = 1; /* Default else-if special processing to on */ s_lab = e_lab = labbuf + 1; s_code = e_code = codebuf + 1; s_com = e_com = combuf + 1; s_token = e_token = tokenbuf + 1; in_buffer = (char *) malloc(10); if (in_buffer == NULL) err(1, NULL); in_buffer_limit = in_buffer + 8; buf_ptr = buf_end = in_buffer; line_no = 1; had_eof = ps.in_decl = ps.decl_on_line = break_comma = false; sp_sw = force_nl = false; ps.in_or_st = false; ps.bl_line = true; dec_ind = 0; di_stack[ps.dec_nest = 0] = 0; ps.want_blank = ps.in_stmt = ps.ind_stmt = false; scase = ps.pcase = false; squest = 0; sc_end = NULL; bp_save = NULL; be_save = NULL; output = NULL; tabs_to_var = 0; envval = getenv("SIMPLE_BACKUP_SUFFIX"); if (envval) simple_backup_suffix = envval; /*--------------------------------------------------*\ | COMMAND LINE SCAN | \*--------------------------------------------------*/ #ifdef undef max_col = 78; /* -l78 */ lineup_to_parens = 1; /* -lp */ lineup_to_parens_always = 0; /* -nlpl */ ps.ljust_decl = 0; /* -ndj */ ps.com_ind = 33; /* -c33 */ star_comment_cont = 1; /* -sc */ ps.ind_size = 8; /* -i8 */ verbose = 0; ps.decl_indent = 16; /* -di16 */ ps.local_decl_indent = -1; /* if this is not set to some nonnegative value * by an arg, we will set this equal to * ps.decl_ind */ ps.indent_parameters = 1; /* -ip */ ps.decl_com_ind = 0; /* if this is not set to some positive value * by an arg, we will set this equal to * ps.com_ind */ btype_2 = 1; /* -br */ cuddle_else = 1; /* -ce */ ps.unindent_displace = 0; /* -d0 */ ps.case_indent = 0; /* -cli0 */ format_block_comments = 1; /* -fcb */ format_col1_comments = 1; /* -fc1 */ procnames_start_line = 1; /* -psl */ proc_calls_space = 0; /* -npcs */ comment_delimiter_on_blankline = 1; /* -cdb */ ps.leave_comma = 1; /* -nbc */ #endif for (i = 1; i < argc; ++i) if (strcmp(argv[i], "-npro") == 0) break; else if (argv[i][0] == '-' && argv[i][1] == 'P' && argv[i][2] != '\0') profile_name = argv[i]; /* non-empty -P (set profile) */ set_defaults(); if (i >= argc) set_profile(profile_name); for (i = 1; i < argc; ++i) { /* * look thru args (if any) for changes to defaults */ if (argv[i][0] != '-') {/* no flag on parameter */ if (input == NULL) { /* we must have the input file */ in_name = argv[i]; /* remember name of input file */ input = fopen(in_name, "r"); if (input == NULL) /* check for open error */ err(1, "%s", in_name); continue; } else if (output == NULL) { /* we have the output file */ out_name = argv[i]; /* remember name of output file */ if (strcmp(in_name, out_name) == 0) { /* attempt to overwrite * the file */ errx(1, "input and output files must be different"); } output = fopen(out_name, "w"); if (output == NULL) /* check for create error */ err(1, "%s", out_name); continue; } errx(1, "unknown parameter: %s", argv[i]); } else set_option(argv[i]); } /* end of for */ if (input == NULL) input = stdin; if (output == NULL) { if (input == stdin) output = stdout; else { out_name = in_name; bakcopy(); } } /* Restrict input/output descriptors and enter Capsicum sandbox. */ cap_rights_init(&rights, CAP_FSTAT, CAP_WRITE); if (caph_rights_limit(fileno(output), &rights) < 0) err(EXIT_FAILURE, "unable to limit rights for %s", out_name); cap_rights_init(&rights, CAP_FSTAT, CAP_READ); if (caph_rights_limit(fileno(input), &rights) < 0) err(EXIT_FAILURE, "unable to limit rights for %s", in_name); if (caph_enter() < 0) err(EXIT_FAILURE, "unable to enter capability mode"); if (opt.com_ind <= 1) opt.com_ind = 2; /* don't put normal comments before column 2 */ if (opt.block_comment_max_col <= 0) opt.block_comment_max_col = opt.max_col; if (opt.local_decl_indent < 0) /* if not specified by user, set this */ opt.local_decl_indent = opt.decl_indent; if (opt.decl_com_ind <= 0) /* if not specified by user, set this */ opt.decl_com_ind = opt.ljust_decl ? (opt.com_ind <= 10 ? 2 : opt.com_ind - 8) : opt.com_ind; if (opt.continuation_indent == 0) opt.continuation_indent = opt.ind_size; fill_buffer(); /* get first batch of stuff into input buffer */ parse(semicolon); { char *p = buf_ptr; int col = 1; while (1) { if (*p == ' ') col++; else if (*p == '\t') col = opt.tabsize * (1 + (col - 1) / opt.tabsize) + 1; else break; p++; } if (col > opt.ind_size) ps.ind_level = ps.i_l_follow = col / opt.ind_size; } /* * START OF MAIN LOOP */ while (1) { /* this is the main loop. it will go until we * reach eof */ int comment_buffered = false; type_code = lexi(&ps); /* lexi reads one token. The actual * characters read are stored in "token". lexi * returns a code indicating the type of token */ /* * The following code moves newlines and comments following an if (), * while (), else, etc. up to the start of the following stmt to * a buffer. This allows proper handling of both kinds of brace * placement (-br, -bl) and cuddling "else" (-ce). */ while (ps.search_brace) { switch (type_code) { case newline: if (sc_end == NULL) { save_com = sc_buf; save_com[0] = save_com[1] = ' '; sc_end = &save_com[2]; } *sc_end++ = '\n'; /* * We may have inherited a force_nl == true from the previous * token (like a semicolon). But once we know that a newline * has been scanned in this loop, force_nl should be false. * * However, the force_nl == true must be preserved if newline * is never scanned in this loop, so this assignment cannot be * done earlier. */ force_nl = false; case form_feed: break; case comment: if (sc_end == NULL) { /* * Copy everything from the start of the line, because * pr_comment() will use that to calculate original * indentation of a boxed comment. */ memcpy(sc_buf, in_buffer, buf_ptr - in_buffer - 4); save_com = sc_buf + (buf_ptr - in_buffer - 4); save_com[0] = save_com[1] = ' '; sc_end = &save_com[2]; } comment_buffered = true; *sc_end++ = '/'; /* copy in start of comment */ *sc_end++ = '*'; for (;;) { /* loop until we get to the end of the comment */ *sc_end = *buf_ptr++; if (buf_ptr >= buf_end) fill_buffer(); if (*sc_end++ == '*' && *buf_ptr == '/') break; /* we are at end of comment */ if (sc_end >= &save_com[sc_size]) { /* check for temp buffer * overflow */ diag2(1, "Internal buffer overflow - Move big comment from right after if, while, or whatever"); fflush(output); exit(1); } } *sc_end++ = '/'; /* add ending slash */ if (++buf_ptr >= buf_end) /* get past / in buffer */ fill_buffer(); break; case lbrace: /* * Put KNF-style lbraces before the buffered up tokens and * jump out of this loop in order to avoid copying the token * again under the default case of the switch below. */ if (sc_end != NULL && opt.btype_2) { save_com[0] = '{'; /* * Originally the lbrace may have been alone on its own * line, but it will be moved into "the else's line", so * if there was a newline resulting from the "{" before, * it must be scanned now and ignored. */ while (isspace((unsigned char)*buf_ptr)) { if (++buf_ptr >= buf_end) fill_buffer(); if (*buf_ptr == '\n') break; } goto sw_buffer; } /* FALLTHROUGH */ default: /* it is the start of a normal statement */ { int remove_newlines; remove_newlines = /* "} else" */ (type_code == sp_nparen && *token == 'e' && e_code != s_code && e_code[-1] == '}') /* "else if" */ || (type_code == sp_paren && *token == 'i' && last_else && opt.else_if); if (remove_newlines) force_nl = false; if (sc_end == NULL) { /* ignore buffering if * comment wasn't saved up */ ps.search_brace = false; goto check_type; } while (sc_end > save_com && isblank((unsigned char)sc_end[-1])) { sc_end--; } if (opt.swallow_optional_blanklines || (!comment_buffered && remove_newlines)) { force_nl = !remove_newlines; while (sc_end > save_com && sc_end[-1] == '\n') { sc_end--; } } if (force_nl) { /* if we should insert a nl here, put * it into the buffer */ force_nl = false; --line_no; /* this will be re-increased when the * newline is read from the buffer */ *sc_end++ = '\n'; *sc_end++ = ' '; if (opt.verbose) /* print error msg if the line was * not already broken */ diag2(0, "Line broken"); } for (t_ptr = token; *t_ptr; ++t_ptr) *sc_end++ = *t_ptr; sw_buffer: ps.search_brace = false; /* stop looking for start of * stmt */ bp_save = buf_ptr; /* save current input buffer */ be_save = buf_end; buf_ptr = save_com; /* fix so that subsequent calls to * lexi will take tokens out of * save_com */ *sc_end++ = ' ';/* add trailing blank, just in case */ buf_end = sc_end; sc_end = NULL; break; } } /* end of switch */ /* * We must make this check, just in case there was an unexpected * EOF. */ if (type_code != 0) { /* * The only intended purpose of calling lexi() below is to * categorize the next token in order to decide whether to * continue buffering forthcoming tokens. Once the buffering * is over, lexi() will be called again elsewhere on all of * the tokens - this time for normal processing. * * Calling it for this purpose is a bug, because lexi() also * changes the parser state and discards leading whitespace, * which is needed mostly for comment-related considerations. * * Work around the former problem by giving lexi() a copy of * the current parser state and discard it if the call turned * out to be just a look ahead. * * Work around the latter problem by copying all whitespace * characters into the buffer so that the later lexi() call * will read them. */ if (sc_end != NULL) { while (*buf_ptr == ' ' || *buf_ptr == '\t') { *sc_end++ = *buf_ptr++; if (sc_end >= &save_com[sc_size]) { errx(1, "input too long"); } } if (buf_ptr >= buf_end) { fill_buffer(); } } transient_state = ps; type_code = lexi(&transient_state); /* read another token */ if (type_code != newline && type_code != form_feed && type_code != comment && !transient_state.search_brace) { ps = transient_state; } } } /* end of while (search_brace) */ last_else = 0; check_type: if (type_code == 0) { /* we got eof */ if (s_lab != e_lab || s_code != e_code || s_com != e_com) /* must dump end of line */ dump_line(); if (ps.tos > 1) /* check for balanced braces */ diag2(1, "Stuff missing from end of file"); if (opt.verbose) { printf("There were %d output lines and %d comments\n", ps.out_lines, ps.out_coms); printf("(Lines with comments)/(Lines with code): %6.3f\n", (1.0 * ps.com_lines) / code_lines); } fflush(output); exit(found_err); } if ( (type_code != comment) && (type_code != newline) && (type_code != preesc) && (type_code != form_feed)) { if (force_nl && (type_code != semicolon) && (type_code != lbrace || !opt.btype_2)) { /* we should force a broken line here */ if (opt.verbose) diag2(0, "Line broken"); dump_line(); ps.want_blank = false; /* dont insert blank at line start */ force_nl = false; } ps.in_stmt = true; /* turn on flag which causes an extra level of * indentation. this is turned off by a ; or * '}' */ if (s_com != e_com) { /* the turkey has embedded a comment * in a line. fix it */ int len = e_com - s_com; CHECK_SIZE_CODE(len + 3); *e_code++ = ' '; memcpy(e_code, s_com, len); e_code += len; *e_code++ = ' '; *e_code = '\0'; /* null terminate code sect */ ps.want_blank = false; e_com = s_com; } } else if (type_code != comment) /* preserve force_nl thru a comment */ force_nl = false; /* cancel forced newline after newline, form * feed, etc */ /*-----------------------------------------------------*\ | do switch on type of token scanned | \*-----------------------------------------------------*/ CHECK_SIZE_CODE(3); /* maximum number of increments of e_code * before the next CHECK_SIZE_CODE or * dump_line() is 2. After that there's the * final increment for the null character. */ switch (type_code) { /* now, decide what to do with the token */ case form_feed: /* found a form feed in line */ ps.use_ff = true; /* a form feed is treated much like a newline */ dump_line(); ps.want_blank = false; break; case newline: if (ps.last_token != comma || ps.p_l_follow > 0 || !opt.leave_comma || ps.block_init || !break_comma || s_com != e_com) { dump_line(); ps.want_blank = false; } ++line_no; /* keep track of input line number */ break; case lparen: /* got a '(' or '[' */ /* count parens to make Healy happy */ if (++ps.p_l_follow == nitems(ps.paren_indents)) { diag3(0, "Reached internal limit of %d unclosed parens", nitems(ps.paren_indents)); ps.p_l_follow--; } if (*token == '[') /* not a function pointer declaration or a function call */; else if (ps.in_decl && !ps.block_init && !ps.dumped_decl_indent && ps.procname[0] == '\0' && ps.paren_level == 0) { /* function pointer declarations */ indent_declaration(dec_ind, tabs_to_var); ps.dumped_decl_indent = true; } else if (ps.want_blank && ((ps.last_token != ident && ps.last_token != funcname) || opt.proc_calls_space || /* offsetof (1) is never allowed a space; sizeof (2) gets * one iff -bs; all other keywords (>2) always get a space * before lparen */ ps.keyword + opt.Bill_Shannon > 2)) *e_code++ = ' '; ps.want_blank = false; *e_code++ = token[0]; ps.paren_indents[ps.p_l_follow - 1] = count_spaces_until(1, s_code, e_code) - 1; if (sp_sw && ps.p_l_follow == 1 && opt.extra_expression_indent && ps.paren_indents[0] < 2 * opt.ind_size) ps.paren_indents[0] = 2 * opt.ind_size; if (ps.in_or_st && *token == '(' && ps.tos <= 2) { /* * this is a kluge to make sure that declarations will be * aligned right if proc decl has an explicit type on it, i.e. * "int a(x) {..." */ parse(semicolon); /* I said this was a kluge... */ ps.in_or_st = false; /* turn off flag for structure decl or * initialization */ } /* parenthesized type following sizeof or offsetof is not a cast */ if (ps.keyword == 1 || ps.keyword == 2) ps.not_cast_mask |= 1 << ps.p_l_follow; break; case rparen: /* got a ')' or ']' */ if (ps.cast_mask & (1 << ps.p_l_follow) & ~ps.not_cast_mask) { ps.last_u_d = true; ps.cast_mask &= (1 << ps.p_l_follow) - 1; ps.want_blank = opt.space_after_cast; } else ps.want_blank = true; ps.not_cast_mask &= (1 << ps.p_l_follow) - 1; if (--ps.p_l_follow < 0) { ps.p_l_follow = 0; diag3(0, "Extra %c", *token); } if (e_code == s_code) /* if the paren starts the line */ ps.paren_level = ps.p_l_follow; /* then indent it */ *e_code++ = token[0]; if (sp_sw && (ps.p_l_follow == 0)) { /* check for end of if * (...), or some such */ sp_sw = false; force_nl = true;/* must force newline after if */ ps.last_u_d = true; /* inform lexi that a following * operator is unary */ ps.in_stmt = false; /* dont use stmt continuation * indentation */ parse(hd_type); /* let parser worry about if, or whatever */ } ps.search_brace = opt.btype_2; /* this should ensure that * constructs such as main(){...} * and int[]{...} have their braces * put in the right place */ break; case unary_op: /* this could be any unary operation */ if (!ps.dumped_decl_indent && ps.in_decl && !ps.block_init && ps.procname[0] == '\0' && ps.paren_level == 0) { /* pointer declarations */ /* * if this is a unary op in a declaration, we should indent * this token */ for (i = 0; token[i]; ++i) /* find length of token */; indent_declaration(dec_ind - i, tabs_to_var); ps.dumped_decl_indent = true; } else if (ps.want_blank) *e_code++ = ' '; { int len = e_token - s_token; CHECK_SIZE_CODE(len); memcpy(e_code, token, len); e_code += len; } ps.want_blank = false; break; case binary_op: /* any binary operation */ { int len = e_token - s_token; CHECK_SIZE_CODE(len + 1); if (ps.want_blank) *e_code++ = ' '; memcpy(e_code, token, len); e_code += len; } ps.want_blank = true; break; case postop: /* got a trailing ++ or -- */ *e_code++ = token[0]; *e_code++ = token[1]; ps.want_blank = true; break; case question: /* got a ? */ squest++; /* this will be used when a later colon * appears so we can distinguish the * ?: construct */ if (ps.want_blank) *e_code++ = ' '; *e_code++ = '?'; ps.want_blank = true; break; case casestmt: /* got word 'case' or 'default' */ scase = true; /* so we can process the later colon properly */ goto copy_id; case colon: /* got a ':' */ if (squest > 0) { /* it is part of the ?: construct */ --squest; if (ps.want_blank) *e_code++ = ' '; *e_code++ = ':'; ps.want_blank = true; break; } if (ps.in_or_st) { *e_code++ = ':'; ps.want_blank = false; break; } ps.in_stmt = false; /* seeing a label does not imply we are in a * stmt */ /* * turn everything so far into a label */ { int len = e_code - s_code; CHECK_SIZE_LAB(len + 3); memcpy(e_lab, s_code, len); e_lab += len; *e_lab++ = ':'; *e_lab = '\0'; e_code = s_code; } force_nl = ps.pcase = scase; /* ps.pcase will be used by * dump_line to decide how to * indent the label. force_nl * will force a case n: to be * on a line by itself */ scase = false; ps.want_blank = false; break; case semicolon: /* got a ';' */ if (ps.dec_nest == 0) ps.in_or_st = false;/* we are not in an initialization or * structure declaration */ scase = false; /* these will only need resetting in an error */ squest = 0; if (ps.last_token == rparen) ps.in_parameter_declaration = 0; ps.cast_mask = 0; ps.not_cast_mask = 0; ps.block_init = 0; ps.block_init_level = 0; ps.just_saw_decl--; if (ps.in_decl && s_code == e_code && !ps.block_init && !ps.dumped_decl_indent && ps.paren_level == 0) { /* indent stray semicolons in declarations */ indent_declaration(dec_ind - 1, tabs_to_var); ps.dumped_decl_indent = true; } ps.in_decl = (ps.dec_nest > 0); /* if we were in a first level * structure declaration, we * arent any more */ if ((!sp_sw || hd_type != forstmt) && ps.p_l_follow > 0) { /* * This should be true iff there were unbalanced parens in the * stmt. It is a bit complicated, because the semicolon might * be in a for stmt */ diag2(1, "Unbalanced parens"); ps.p_l_follow = 0; if (sp_sw) { /* this is a check for an if, while, etc. with * unbalanced parens */ sp_sw = false; parse(hd_type); /* dont lose the if, or whatever */ } } *e_code++ = ';'; ps.want_blank = true; ps.in_stmt = (ps.p_l_follow > 0); /* we are no longer in the * middle of a stmt */ if (!sp_sw) { /* if not if for (;;) */ parse(semicolon); /* let parser know about end of stmt */ force_nl = true;/* force newline after an end of stmt */ } break; case lbrace: /* got a '{' */ ps.in_stmt = false; /* dont indent the {} */ if (!ps.block_init) force_nl = true;/* force other stuff on same line as '{' onto * new line */ else if (ps.block_init_level <= 0) ps.block_init_level = 1; else ps.block_init_level++; if (s_code != e_code && !ps.block_init) { if (!opt.btype_2) { dump_line(); ps.want_blank = false; } else if (ps.in_parameter_declaration && !ps.in_or_st) { ps.i_l_follow = 0; if (opt.function_brace_split) { /* dump the line prior * to the brace ... */ dump_line(); ps.want_blank = false; } else /* add a space between the decl and brace */ ps.want_blank = true; } } if (ps.in_parameter_declaration) prefix_blankline_requested = 0; if (ps.p_l_follow > 0) { /* check for preceding unbalanced * parens */ diag2(1, "Unbalanced parens"); ps.p_l_follow = 0; if (sp_sw) { /* check for unclosed if, for, etc. */ sp_sw = false; parse(hd_type); ps.ind_level = ps.i_l_follow; } } if (s_code == e_code) ps.ind_stmt = false; /* dont put extra indentation on line * with '{' */ if (ps.in_decl && ps.in_or_st) { /* this is either a structure * declaration or an init */ di_stack[ps.dec_nest] = dec_ind; if (++ps.dec_nest == nitems(di_stack)) { diag3(0, "Reached internal limit of %d struct levels", nitems(di_stack)); ps.dec_nest--; } /* ? dec_ind = 0; */ } else { ps.decl_on_line = false; /* we can't be in the middle of * a declaration, so don't do * special indentation of * comments */ if (opt.blanklines_after_declarations_at_proctop && ps.in_parameter_declaration) postfix_blankline_requested = 1; ps.in_parameter_declaration = 0; ps.in_decl = false; } dec_ind = 0; parse(lbrace); /* let parser know about this */ if (ps.want_blank) /* put a blank before '{' if '{' is not at * start of line */ *e_code++ = ' '; ps.want_blank = false; *e_code++ = '{'; ps.just_saw_decl = 0; break; case rbrace: /* got a '}' */ if (ps.p_stack[ps.tos] == decl && !ps.block_init) /* semicolons can be * omitted in * declarations */ parse(semicolon); if (ps.p_l_follow) {/* check for unclosed if, for, else. */ diag2(1, "Unbalanced parens"); ps.p_l_follow = 0; sp_sw = false; } ps.just_saw_decl = 0; ps.block_init_level--; if (s_code != e_code && !ps.block_init) { /* '}' must be first on * line */ if (opt.verbose) diag2(0, "Line broken"); dump_line(); } *e_code++ = '}'; ps.want_blank = true; ps.in_stmt = ps.ind_stmt = false; if (ps.dec_nest > 0) { /* we are in multi-level structure * declaration */ dec_ind = di_stack[--ps.dec_nest]; if (ps.dec_nest == 0 && !ps.in_parameter_declaration) ps.just_saw_decl = 2; ps.in_decl = true; } prefix_blankline_requested = 0; parse(rbrace); /* let parser know about this */ ps.search_brace = opt.cuddle_else && ps.p_stack[ps.tos] == ifhead && ps.il[ps.tos] >= ps.ind_level; if (ps.tos <= 1 && opt.blanklines_after_procs && ps.dec_nest <= 0) postfix_blankline_requested = 1; break; case swstmt: /* got keyword "switch" */ sp_sw = true; hd_type = swstmt; /* keep this for when we have seen the * expression */ goto copy_id; /* go move the token into buffer */ case sp_paren: /* token is if, while, for */ sp_sw = true; /* the interesting stuff is done after the * expression is scanned */ hd_type = (*token == 'i' ? ifstmt : (*token == 'w' ? whilestmt : forstmt)); /* * remember the type of header for later use by parser */ goto copy_id; /* copy the token into line */ case sp_nparen: /* got else, do */ ps.in_stmt = false; if (*token == 'e') { if (e_code != s_code && (!opt.cuddle_else || e_code[-1] != '}')) { if (opt.verbose) diag2(0, "Line broken"); dump_line();/* make sure this starts a line */ ps.want_blank = false; } force_nl = true;/* also, following stuff must go onto new line */ last_else = 1; parse(elselit); } else { if (e_code != s_code) { /* make sure this starts a line */ if (opt.verbose) diag2(0, "Line broken"); dump_line(); ps.want_blank = false; } force_nl = true;/* also, following stuff must go onto new line */ last_else = 0; parse(dolit); } goto copy_id; /* move the token into line */ case type_def: case storage: prefix_blankline_requested = 0; goto copy_id; case structure: if (ps.p_l_follow > 0) goto copy_id; /* FALLTHROUGH */ case decl: /* we have a declaration type (int, etc.) */ parse(decl); /* let parser worry about indentation */ if (ps.last_token == rparen && ps.tos <= 1) { if (s_code != e_code) { dump_line(); ps.want_blank = 0; } } if (ps.in_parameter_declaration && opt.indent_parameters && ps.dec_nest == 0) { ps.ind_level = ps.i_l_follow = 1; ps.ind_stmt = 0; } ps.in_or_st = true; /* this might be a structure or initialization * declaration */ ps.in_decl = ps.decl_on_line = ps.last_token != type_def; if ( /* !ps.in_or_st && */ ps.dec_nest <= 0) ps.just_saw_decl = 2; prefix_blankline_requested = 0; for (i = 0; token[i++];); /* get length of token */ if (ps.ind_level == 0 || ps.dec_nest > 0) { /* global variable or struct member in local variable */ dec_ind = opt.decl_indent > 0 ? opt.decl_indent : i; tabs_to_var = (opt.use_tabs ? opt.decl_indent > 0 : 0); } else { /* local variable */ dec_ind = opt.local_decl_indent > 0 ? opt.local_decl_indent : i; tabs_to_var = (opt.use_tabs ? opt.local_decl_indent > 0 : 0); } goto copy_id; case funcname: case ident: /* got an identifier or constant */ if (ps.in_decl) { if (type_code == funcname) { ps.in_decl = false; if (opt.procnames_start_line && s_code != e_code) { *e_code = '\0'; dump_line(); } else if (ps.want_blank) { *e_code++ = ' '; } ps.want_blank = false; } else if (!ps.block_init && !ps.dumped_decl_indent && ps.paren_level == 0) { /* if we are in a declaration, we * must indent identifier */ indent_declaration(dec_ind, tabs_to_var); ps.dumped_decl_indent = true; ps.want_blank = false; } } else if (sp_sw && ps.p_l_follow == 0) { sp_sw = false; force_nl = true; ps.last_u_d = true; ps.in_stmt = false; parse(hd_type); } copy_id: { int len = e_token - s_token; CHECK_SIZE_CODE(len + 1); if (ps.want_blank) *e_code++ = ' '; memcpy(e_code, s_token, len); e_code += len; } if (type_code != funcname) ps.want_blank = true; break; case strpfx: { int len = e_token - s_token; CHECK_SIZE_CODE(len + 1); if (ps.want_blank) *e_code++ = ' '; memcpy(e_code, token, len); e_code += len; } ps.want_blank = false; break; case period: /* treat a period kind of like a binary * operation */ *e_code++ = '.'; /* move the period into line */ ps.want_blank = false; /* dont put a blank after a period */ break; case comma: ps.want_blank = (s_code != e_code); /* only put blank after comma * if comma does not start the * line */ if (ps.in_decl && ps.procname[0] == '\0' && !ps.block_init && !ps.dumped_decl_indent && ps.paren_level == 0) { /* indent leading commas and not the actual identifiers */ indent_declaration(dec_ind - 1, tabs_to_var); ps.dumped_decl_indent = true; } *e_code++ = ','; if (ps.p_l_follow == 0) { if (ps.block_init_level <= 0) ps.block_init = 0; if (break_comma && (!opt.leave_comma || count_spaces_until(compute_code_target(), s_code, e_code) > opt.max_col - opt.tabsize)) force_nl = true; } break; case preesc: /* got the character '#' */ if ((s_com != e_com) || (s_lab != e_lab) || (s_code != e_code)) dump_line(); CHECK_SIZE_LAB(1); *e_lab++ = '#'; /* move whole line to 'label' buffer */ { int in_comment = 0; int com_start = 0; char quote = 0; int com_end = 0; while (*buf_ptr == ' ' || *buf_ptr == '\t') { buf_ptr++; if (buf_ptr >= buf_end) fill_buffer(); } while (*buf_ptr != '\n' || (in_comment && !had_eof)) { CHECK_SIZE_LAB(2); *e_lab = *buf_ptr++; if (buf_ptr >= buf_end) fill_buffer(); switch (*e_lab++) { case BACKSLASH: if (!in_comment) { *e_lab++ = *buf_ptr++; if (buf_ptr >= buf_end) fill_buffer(); } break; case '/': if (*buf_ptr == '*' && !in_comment && !quote) { in_comment = 1; *e_lab++ = *buf_ptr++; com_start = e_lab - s_lab - 2; } break; case '"': if (quote == '"') quote = 0; break; case '\'': if (quote == '\'') quote = 0; break; case '*': if (*buf_ptr == '/' && in_comment) { in_comment = 0; *e_lab++ = *buf_ptr++; com_end = e_lab - s_lab; } break; } } while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t')) e_lab--; if (e_lab - s_lab == com_end && bp_save == NULL) { /* comment on preprocessor line */ if (sc_end == NULL) { /* if this is the first comment, * we must set up the buffer */ save_com = sc_buf; sc_end = &save_com[0]; } else { *sc_end++ = '\n'; /* add newline between * comments */ *sc_end++ = ' '; --line_no; } if (sc_end - save_com + com_end - com_start > sc_size) errx(1, "input too long"); memmove(sc_end, s_lab + com_start, com_end - com_start); sc_end += com_end - com_start; e_lab = s_lab + com_start; while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t')) e_lab--; bp_save = buf_ptr; /* save current input buffer */ be_save = buf_end; buf_ptr = save_com; /* fix so that subsequent calls to * lexi will take tokens out of * save_com */ *sc_end++ = ' '; /* add trailing blank, just in case */ buf_end = sc_end; sc_end = NULL; } CHECK_SIZE_LAB(1); *e_lab = '\0'; /* null terminate line */ ps.pcase = false; } if (strncmp(s_lab, "#if", 3) == 0) { /* also ifdef, ifndef */ if ((size_t)ifdef_level < nitems(state_stack)) { match_state[ifdef_level].tos = -1; state_stack[ifdef_level++] = ps; } else diag2(1, "#if stack overflow"); } else if (strncmp(s_lab, "#el", 3) == 0) { /* else, elif */ if (ifdef_level <= 0) diag2(1, s_lab[3] == 'i' ? "Unmatched #elif" : "Unmatched #else"); else { match_state[ifdef_level - 1] = ps; ps = state_stack[ifdef_level - 1]; } } else if (strncmp(s_lab, "#endif", 6) == 0) { if (ifdef_level <= 0) diag2(1, "Unmatched #endif"); else ifdef_level--; } else { struct directives { int size; const char *string; } recognized[] = { {7, "include"}, {6, "define"}, {5, "undef"}, {4, "line"}, {5, "error"}, {6, "pragma"} }; int d = nitems(recognized); while (--d >= 0) if (strncmp(s_lab + 1, recognized[d].string, recognized[d].size) == 0) break; if (d < 0) { diag2(1, "Unrecognized cpp directive"); break; } } if (opt.blanklines_around_conditional_compilation) { postfix_blankline_requested++; n_real_blanklines = 0; } else { postfix_blankline_requested = 0; prefix_blankline_requested = 0; } break; /* subsequent processing of the newline * character will cause the line to be printed */ case comment: /* we have gotten a / followed by * this is a biggie */ pr_comment(); break; } /* end of big switch stmt */ *e_code = '\0'; /* make sure code section is null terminated */ if (type_code != comment && type_code != newline && type_code != preesc) ps.last_token = type_code; } /* end of main while (1) loop */ } /* * copy input file to backup file if in_name is /blah/blah/blah/file, then * backup file will be ".Bfile" then make the backup file the input and * original input file the output */ static void bakcopy(void) { int n, bakchn; char buff[8 * 1024]; const char *p; /* construct file name .Bfile */ for (p = in_name; *p; p++); /* skip to end of string */ while (p > in_name && *p != '/') /* find last '/' */ p--; if (*p == '/') p++; sprintf(bakfile, "%s%s", p, simple_backup_suffix); /* copy in_name to backup file */ bakchn = creat(bakfile, 0600); if (bakchn < 0) err(1, "%s", bakfile); while ((n = read(fileno(input), buff, sizeof(buff))) > 0) if (write(bakchn, buff, n) != n) err(1, "%s", bakfile); if (n < 0) err(1, "%s", in_name); close(bakchn); fclose(input); /* re-open backup file as the input file */ input = fopen(bakfile, "r"); if (input == NULL) err(1, "%s", bakfile); /* now the original input file will be the output */ output = fopen(in_name, "w"); if (output == NULL) { unlink(bakfile); err(1, "%s", in_name); } } static void indent_declaration(int cur_dec_ind, int tabs_to_var) { int pos = e_code - s_code; char *startpos = e_code; /* * get the tab math right for indentations that are not multiples of tabsize */ if ((ps.ind_level * opt.ind_size) % opt.tabsize != 0) { pos += (ps.ind_level * opt.ind_size) % opt.tabsize; cur_dec_ind += (ps.ind_level * opt.ind_size) % opt.tabsize; } if (tabs_to_var) { int tpos; CHECK_SIZE_CODE(cur_dec_ind / opt.tabsize); while ((tpos = opt.tabsize * (1 + pos / opt.tabsize)) <= cur_dec_ind) { *e_code++ = '\t'; pos = tpos; } } CHECK_SIZE_CODE(cur_dec_ind - pos + 1); while (pos < cur_dec_ind) { *e_code++ = ' '; pos++; } if (e_code == startpos && ps.want_blank) { *e_code++ = ' '; ps.want_blank = false; } } diff --git a/usr.bin/indent/indent.h b/usr.bin/indent/indent.h index bbbb5dc805dd..1f9ceac11253 100644 --- a/usr.bin/indent/indent.h +++ b/usr.bin/indent/indent.h @@ -1,49 +1,46 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2001 Jens Schweikhardt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 -#endif - void add_typename(const char *); void alloc_typenames(void); int compute_code_target(void); int compute_label_target(void); int count_spaces(int, char *); int count_spaces_until(int, char *, char *); void init_constant_tt(void); int lexi(struct parser_state *); void diag2(int, const char *); void diag3(int, const char *, int); void diag4(int, const char *, int, int); void dump_line(void); void fill_buffer(void); void parse(int); void pr_comment(void); void set_defaults(void); void set_option(char *); void set_profile(const char *); diff --git a/usr.bin/indent/io.c b/usr.bin/indent/io.c index e4a87213a0a1..61b87c9343ce 100644 --- a/usr.bin/indent/io.c +++ b/usr.bin/indent/io.c @@ -1,532 +1,529 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1985 Sun Microsystems, Inc. * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * 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 -#endif - #include #include #include #include #include #include #include "indent_globs.h" #include "indent.h" /* Globals */ int found_err; int n_real_blanklines; int prefix_blankline_requested, postfix_blankline_requested; int code_lines; int had_eof; int inhibit_formatting; int suppress_blanklines; int comment_open; static int paren_target; static int pad_output(int current, int target); void dump_line(void) { /* dump_line is the routine that actually * effects the printing of the new source. It * prints the label section, followed by the * code section with the appropriate nesting * level, followed by any comments */ int cur_col, target_col = 1; static int not_first_line; if (ps.procname[0]) { ps.ind_level = 0; ps.procname[0] = 0; } if (s_code == e_code && s_lab == e_lab && s_com == e_com) { if (suppress_blanklines > 0) suppress_blanklines--; else { ps.bl_line = true; n_real_blanklines++; } } else if (!inhibit_formatting) { suppress_blanklines = 0; ps.bl_line = false; if (prefix_blankline_requested && not_first_line) { if (opt.swallow_optional_blanklines) { if (n_real_blanklines == 1) n_real_blanklines = 0; } else { if (n_real_blanklines == 0) n_real_blanklines = 1; } } while (--n_real_blanklines >= 0) putc('\n', output); n_real_blanklines = 0; if (ps.ind_level == 0) ps.ind_stmt = 0; /* this is a class A kludge. dont do * additional statement indentation if we are * at bracket level 0 */ if (e_lab != s_lab || e_code != s_code) ++code_lines; /* keep count of lines with code */ if (e_lab != s_lab) { /* print lab, if any */ if (comment_open) { comment_open = 0; fprintf(output, ".*/\n"); } while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t')) e_lab--; *e_lab = '\0'; cur_col = pad_output(1, compute_label_target()); if (s_lab[0] == '#' && (strncmp(s_lab, "#else", 5) == 0 || strncmp(s_lab, "#endif", 6) == 0)) { char *s = s_lab; if (e_lab[-1] == '\n') e_lab--; do putc(*s++, output); while (s < e_lab && 'a' <= *s && *s<='z'); while ((*s == ' ' || *s == '\t') && s < e_lab) s++; if (s < e_lab) fprintf(output, s[0]=='/' && s[1]=='*' ? "\t%.*s" : "\t/* %.*s */", (int)(e_lab - s), s); } else fprintf(output, "%.*s", (int)(e_lab - s_lab), s_lab); cur_col = count_spaces(cur_col, s_lab); } else cur_col = 1; /* there is no label section */ ps.pcase = false; if (s_code != e_code) { /* print code section, if any */ char *p; if (comment_open) { comment_open = 0; fprintf(output, ".*/\n"); } target_col = compute_code_target(); { int i; for (i = 0; i < ps.p_l_follow; i++) if (ps.paren_indents[i] >= 0) ps.paren_indents[i] = -(ps.paren_indents[i] + target_col); } cur_col = pad_output(cur_col, target_col); for (p = s_code; p < e_code; p++) if (*p == (char) 0200) fprintf(output, "%d", target_col * 7); else putc(*p, output); cur_col = count_spaces(cur_col, s_code); } if (s_com != e_com) { /* print comment, if any */ int target = ps.com_col; char *com_st = s_com; target += ps.comment_delta; while (*com_st == '\t') /* consider original indentation in * case this is a box comment */ com_st++, target += opt.tabsize; while (target <= 0) if (*com_st == ' ') target++, com_st++; else if (*com_st == '\t') { target = opt.tabsize * (1 + (target - 1) / opt.tabsize) + 1; com_st++; } else target = 1; if (cur_col > target) { /* if comment can't fit on this line, * put it on next line */ putc('\n', output); cur_col = 1; ++ps.out_lines; } while (e_com > com_st && isspace((unsigned char)e_com[-1])) e_com--; (void)pad_output(cur_col, target); fwrite(com_st, e_com - com_st, 1, output); ps.comment_delta = ps.n_comment_delta; ++ps.com_lines; /* count lines with comments */ } if (ps.use_ff) putc('\014', output); else putc('\n', output); ++ps.out_lines; if (ps.just_saw_decl == 1 && opt.blanklines_after_declarations) { prefix_blankline_requested = 1; ps.just_saw_decl = 0; } else prefix_blankline_requested = postfix_blankline_requested; postfix_blankline_requested = 0; } ps.decl_on_line = ps.in_decl; /* if we are in the middle of a * declaration, remember that fact for * proper comment indentation */ ps.ind_stmt = ps.in_stmt & ~ps.in_decl; /* next line should be * indented if we have not * completed this stmt and if * we are not in the middle of * a declaration */ ps.use_ff = false; ps.dumped_decl_indent = 0; *(e_lab = s_lab) = '\0'; /* reset buffers */ *(e_code = s_code) = '\0'; *(e_com = s_com = combuf + 1) = '\0'; ps.ind_level = ps.i_l_follow; ps.paren_level = ps.p_l_follow; if (ps.paren_level > 0) paren_target = -ps.paren_indents[ps.paren_level - 1]; not_first_line = 1; } int compute_code_target(void) { int target_col = opt.ind_size * ps.ind_level + 1; if (ps.paren_level) if (!opt.lineup_to_parens) target_col += opt.continuation_indent * (2 * opt.continuation_indent == opt.ind_size ? 1 : ps.paren_level); else if (opt.lineup_to_parens_always) target_col = paren_target; else { int w; int t = paren_target; if ((w = count_spaces(t, s_code) - opt.max_col) > 0 && count_spaces(target_col, s_code) <= opt.max_col) { t -= w + 1; if (t > target_col) target_col = t; } else target_col = t; } else if (ps.ind_stmt) target_col += opt.continuation_indent; return target_col; } int compute_label_target(void) { return ps.pcase ? (int) (case_ind * opt.ind_size) + 1 : *s_lab == '#' ? 1 : opt.ind_size * (ps.ind_level - label_offset) + 1; } /* * Copyright (C) 1976 by the Board of Trustees of the University of Illinois * * All rights reserved * * * NAME: fill_buffer * * FUNCTION: Reads one block of input into input_buffer * * HISTORY: initial coding November 1976 D A Willcox of CAC 1/7/77 A * Willcox of CAC Added check for switch back to partly full input * buffer from temporary buffer * */ void fill_buffer(void) { /* this routine reads stuff from the input */ char *p; int i; FILE *f = input; if (bp_save != NULL) { /* there is a partly filled input buffer left */ buf_ptr = bp_save; /* do not read anything, just switch buffers */ buf_end = be_save; bp_save = be_save = NULL; if (buf_ptr < buf_end) return; /* only return if there is really something in * this buffer */ } for (p = in_buffer;;) { if (p >= in_buffer_limit) { int size = (in_buffer_limit - in_buffer) * 2 + 10; int offset = p - in_buffer; in_buffer = realloc(in_buffer, size); if (in_buffer == NULL) errx(1, "input line too long"); p = in_buffer + offset; in_buffer_limit = in_buffer + size - 2; } if ((i = getc(f)) == EOF) { *p++ = ' '; *p++ = '\n'; had_eof = true; break; } if (i != '\0') *p++ = i; if (i == '\n') break; } buf_ptr = in_buffer; buf_end = p; if (p - in_buffer > 2 && p[-2] == '/' && p[-3] == '*') { if (in_buffer[3] == 'I' && strncmp(in_buffer, "/**INDENT**", 11) == 0) fill_buffer(); /* flush indent error message */ else { int com = 0; p = in_buffer; while (*p == ' ' || *p == '\t') p++; if (*p == '/' && p[1] == '*') { p += 2; while (*p == ' ' || *p == '\t') p++; if (p[0] == 'I' && p[1] == 'N' && p[2] == 'D' && p[3] == 'E' && p[4] == 'N' && p[5] == 'T') { p += 6; while (*p == ' ' || *p == '\t') p++; if (*p == '*') com = 1; else if (*p == 'O') { if (*++p == 'N') p++, com = 1; else if (*p == 'F' && *++p == 'F') p++, com = 2; } while (*p == ' ' || *p == '\t') p++; if (p[0] == '*' && p[1] == '/' && p[2] == '\n' && com) { if (s_com != e_com || s_lab != e_lab || s_code != e_code) dump_line(); if (!(inhibit_formatting = com - 1)) { n_real_blanklines = 0; postfix_blankline_requested = 0; prefix_blankline_requested = 0; suppress_blanklines = 1; } } } } } } if (inhibit_formatting) { p = in_buffer; do putc(*p, output); while (*p++ != '\n'); } } /* * Copyright (C) 1976 by the Board of Trustees of the University of Illinois * * All rights reserved * * * NAME: pad_output * * FUNCTION: Writes tabs and spaces to move the current column up to the desired * position. * * ALGORITHM: Put tabs and/or blanks into pobuf, then write pobuf. * * PARAMETERS: current integer The current column target * nteger The desired column * * RETURNS: Integer value of the new column. (If current >= target, no action is * taken, and current is returned. * * GLOBALS: None * * CALLS: write (sys) * * CALLED BY: dump_line * * HISTORY: initial coding November 1976 D A Willcox of CAC * */ static int pad_output(int current, int target) /* writes tabs and blanks (if necessary) to * get the current output position up to the * target column */ /* current: the current column value */ /* target: position we want it at */ { int curr; /* internal column pointer */ if (current >= target) return (current); /* line is already long enough */ curr = current; if (opt.use_tabs) { int tcur; while ((tcur = opt.tabsize * (1 + (curr - 1) / opt.tabsize) + 1) <= target) { putc('\t', output); curr = tcur; } } while (curr++ < target) putc(' ', output); /* pad with final blanks */ return (target); } /* * Copyright (C) 1976 by the Board of Trustees of the University of Illinois * * All rights reserved * * * NAME: count_spaces * * FUNCTION: Find out where printing of a given string will leave the current * character position on output. * * ALGORITHM: Run thru input string and add appropriate values to current * position. * * RETURNS: Integer value of position after printing "buffer" starting in column * "current". * * HISTORY: initial coding November 1976 D A Willcox of CAC * */ int count_spaces_until(int cur, char *buffer, char *end) /* * this routine figures out where the character position will be after * printing the text in buffer starting at column "current" */ { char *buf; /* used to look thru buffer */ for (buf = buffer; *buf != '\0' && buf != end; ++buf) { switch (*buf) { case '\n': case 014: /* form feed */ cur = 1; break; case '\t': cur = opt.tabsize * (1 + (cur - 1) / opt.tabsize) + 1; break; case 010: /* backspace */ --cur; break; default: ++cur; break; } /* end of switch */ } /* end of for loop */ return (cur); } int count_spaces(int cur, char *buffer) { return (count_spaces_until(cur, buffer, NULL)); } void diag4(int level, const char *msg, int a, int b) { if (level) found_err = 1; if (output == stdout) { fprintf(stdout, "/**INDENT** %s@%d: ", level == 0 ? "Warning" : "Error", line_no); fprintf(stdout, msg, a, b); fprintf(stdout, " */\n"); } else { fprintf(stderr, "%s@%d: ", level == 0 ? "Warning" : "Error", line_no); fprintf(stderr, msg, a, b); fprintf(stderr, "\n"); } } void diag3(int level, const char *msg, int a) { if (level) found_err = 1; if (output == stdout) { fprintf(stdout, "/**INDENT** %s@%d: ", level == 0 ? "Warning" : "Error", line_no); fprintf(stdout, msg, a); fprintf(stdout, " */\n"); } else { fprintf(stderr, "%s@%d: ", level == 0 ? "Warning" : "Error", line_no); fprintf(stderr, msg, a); fprintf(stderr, "\n"); } } void diag2(int level, const char *msg) { if (level) found_err = 1; if (output == stdout) { fprintf(stdout, "/**INDENT** %s@%d: ", level == 0 ? "Warning" : "Error", line_no); fprintf(stdout, "%s", msg); fprintf(stdout, " */\n"); } else { fprintf(stderr, "%s@%d: ", level == 0 ? "Warning" : "Error", line_no); fprintf(stderr, "%s", msg); fprintf(stderr, "\n"); } } diff --git a/usr.bin/indent/lexi.c b/usr.bin/indent/lexi.c index 21169d513cdd..7883b7c53f36 100644 --- a/usr.bin/indent/lexi.c +++ b/usr.bin/indent/lexi.c @@ -1,650 +1,647 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1985 Sun Microsystems, Inc. * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * 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 -#endif -#include /* * Here we have the token scanner for indent. It scans off one token and puts * it in the global variable "token". It returns a code, indicating the type * of token scanned. */ #include #include #include #include #include #include #include "indent_globs.h" #include "indent_codes.h" #include "indent.h" struct templ { const char *rwd; int rwcode; }; /* * This table has to be sorted alphabetically, because it'll be used in binary * search. For the same reason, string must be the first thing in struct templ. */ struct templ specials[] = { {"_Bool", 4}, {"_Complex", 4}, {"_Imaginary", 4}, {"auto", 10}, {"bool", 4}, {"break", 9}, {"case", 8}, {"char", 4}, {"complex", 4}, {"const", 4}, {"continue", 12}, {"default", 8}, {"do", 6}, {"double", 4}, {"else", 6}, {"enum", 3}, {"extern", 10}, {"float", 4}, {"for", 5}, {"global", 4}, {"goto", 9}, {"if", 5}, {"imaginary", 4}, {"inline", 12}, {"int", 4}, {"long", 4}, {"offsetof", 1}, {"register", 10}, {"restrict", 12}, {"return", 9}, {"short", 4}, {"signed", 4}, {"sizeof", 2}, {"static", 10}, {"struct", 3}, {"switch", 7}, {"typedef", 11}, {"union", 3}, {"unsigned", 4}, {"void", 4}, {"volatile", 4}, {"while", 5} }; const char **typenames; int typename_count; int typename_top = -1; /* * The transition table below was rewritten by hand from lx's output, given * the following definitions. lx is Katherine Flavel's lexer generator. * * O = /[0-7]/; D = /[0-9]/; NZ = /[1-9]/; * H = /[a-f0-9]/i; B = /[0-1]/; HP = /0x/i; * BP = /0b/i; E = /e[+\-]?/i D+; P = /p[+\-]?/i D+; * FS = /[fl]/i; IS = /u/i /(l|L|ll|LL)/? | /(l|L|ll|LL)/ /u/i?; * * D+ E FS? -> $float; * D* "." D+ E? FS? -> $float; * D+ "." E? FS? -> $float; HP H+ IS? -> $int; * HP H+ P FS? -> $float; NZ D* IS? -> $int; * HP H* "." H+ P FS? -> $float; "0" O* IS? -> $int; * HP H+ "." P FS -> $float; BP B+ IS? -> $int; */ static char const *table[] = { /* examples: 00 s 0xx t 00xaa a 11 101100xxa.. r 11ee0001101lbuuxx.a.pp t.01.e+008bLuxll0Ll.aa.p+0 states: ABCDEFGHIJKLMNOPQRSTUVWXYZ */ ['0'] = "CEIDEHHHIJQ U Q VUVVZZZ", ['1'] = "DEIDEHHHIJQ U Q VUVVZZZ", ['7'] = "DEIDEHHHIJ U VUVVZZZ", ['9'] = "DEJDEHHHJJ U VUVVZZZ", ['a'] = " U VUVV ", ['b'] = " K U VUVV ", ['e'] = " FFF FF U VUVV ", ['f'] = " f f U VUVV f", ['u'] = " MM M i iiM M ", ['x'] = " N ", ['p'] = " FFX ", ['L'] = " LLf fL PR Li L f", ['l'] = " OOf fO S P O i O f", ['+'] = " G Y ", ['.'] = "B EE EE T W ", /* ABCDEFGHIJKLMNOPQRSTUVWXYZ */ [0] = "uuiifuufiuuiiuiiiiiuiuuuuu", }; static int strcmp_type(const void *e1, const void *e2) { return (strcmp(e1, *(const char * const *)e2)); } int lexi(struct parser_state *state) { int unary_delim; /* this is set to 1 if the current token * forces a following operator to be unary */ int code; /* internal code to be returned */ char qchar; /* the delimiter character for a string */ e_token = s_token; /* point to start of place to save token */ unary_delim = false; state->col_1 = state->last_nl; /* tell world that this token started * in column 1 iff the last thing * scanned was a newline */ state->last_nl = false; while (*buf_ptr == ' ' || *buf_ptr == '\t') { /* get rid of blanks */ state->col_1 = false; /* leading blanks imply token is not in column * 1 */ if (++buf_ptr >= buf_end) fill_buffer(); } /* Scan an alphanumeric token */ if (isalnum((unsigned char)*buf_ptr) || *buf_ptr == '_' || *buf_ptr == '$' || (buf_ptr[0] == '.' && isdigit((unsigned char)buf_ptr[1]))) { /* * we have a character or number */ struct templ *p; if (isdigit((unsigned char)*buf_ptr) || (buf_ptr[0] == '.' && isdigit((unsigned char)buf_ptr[1]))) { char s; unsigned char i; for (s = 'A'; s != 'f' && s != 'i' && s != 'u'; ) { i = (unsigned char)*buf_ptr; if (i >= nitems(table) || table[i] == NULL || table[i][s - 'A'] == ' ') { s = table[0][s - 'A']; break; } s = table[i][s - 'A']; CHECK_SIZE_TOKEN(1); *e_token++ = *buf_ptr++; if (buf_ptr >= buf_end) fill_buffer(); } /* s now indicates the type: f(loating), i(integer), u(nknown) */ } else while (isalnum((unsigned char)*buf_ptr) || *buf_ptr == BACKSLASH || *buf_ptr == '_' || *buf_ptr == '$') { /* fill_buffer() terminates buffer with newline */ if (*buf_ptr == BACKSLASH) { if (*(buf_ptr + 1) == '\n') { buf_ptr += 2; if (buf_ptr >= buf_end) fill_buffer(); } else break; } CHECK_SIZE_TOKEN(1); /* copy it over */ *e_token++ = *buf_ptr++; if (buf_ptr >= buf_end) fill_buffer(); } *e_token = '\0'; if (s_token[0] == 'L' && s_token[1] == '\0' && (*buf_ptr == '"' || *buf_ptr == '\'')) return (strpfx); while (*buf_ptr == ' ' || *buf_ptr == '\t') { /* get rid of blanks */ if (++buf_ptr >= buf_end) fill_buffer(); } state->keyword = 0; if (state->last_token == structure && !state->p_l_follow) { /* if last token was 'struct' and we're not * in parentheses, then this token * should be treated as a declaration */ state->last_u_d = true; return (decl); } /* * Operator after identifier is binary unless last token was 'struct' */ state->last_u_d = (state->last_token == structure); p = bsearch(s_token, specials, sizeof(specials) / sizeof(specials[0]), sizeof(specials[0]), strcmp_type); if (p == NULL) { /* not a special keyword... */ char *u; /* ... so maybe a type_t or a typedef */ if ((opt.auto_typedefs && ((u = strrchr(s_token, '_')) != NULL) && strcmp(u, "_t") == 0) || (typename_top >= 0 && bsearch(s_token, typenames, typename_top + 1, sizeof(typenames[0]), strcmp_type))) { state->keyword = 4; /* a type name */ state->last_u_d = true; goto found_typename; } } else { /* we have a keyword */ state->keyword = p->rwcode; state->last_u_d = true; switch (p->rwcode) { case 7: /* it is a switch */ return (swstmt); case 8: /* a case or default */ return (casestmt); case 3: /* a "struct" */ /* FALLTHROUGH */ case 4: /* one of the declaration keywords */ found_typename: if (state->p_l_follow) { /* inside parens: cast, param list, offsetof or sizeof */ state->cast_mask |= (1 << state->p_l_follow) & ~state->not_cast_mask; } if (state->last_token == period || state->last_token == unary_op) { state->keyword = 0; break; } if (p != NULL && p->rwcode == 3) return (structure); if (state->p_l_follow) break; return (decl); case 5: /* if, while, for */ return (sp_paren); case 6: /* do, else */ return (sp_nparen); case 10: /* storage class specifier */ return (storage); case 11: /* typedef */ return (type_def); default: /* all others are treated like any other * identifier */ return (ident); } /* end of switch */ } /* end of if (found_it) */ if (*buf_ptr == '(' && state->tos <= 1 && state->ind_level == 0 && state->in_parameter_declaration == 0 && state->block_init == 0) { char *tp = buf_ptr; while (tp < buf_end) if (*tp++ == ')' && (*tp == ';' || *tp == ',')) goto not_proc; strncpy(state->procname, token, sizeof state->procname - 1); if (state->in_decl) state->in_parameter_declaration = 1; return (funcname); not_proc:; } /* * The following hack attempts to guess whether or not the current * token is in fact a declaration keyword -- one that has been * typedefd */ else if (!state->p_l_follow && !state->block_init && !state->in_stmt && ((*buf_ptr == '*' && buf_ptr[1] != '=') || isalpha((unsigned char)*buf_ptr)) && (state->last_token == semicolon || state->last_token == lbrace || state->last_token == rbrace)) { state->keyword = 4; /* a type name */ state->last_u_d = true; return decl; } if (state->last_token == decl) /* if this is a declared variable, * then following sign is unary */ state->last_u_d = true; /* will make "int a -1" work */ return (ident); /* the ident is not in the list */ } /* end of processing for alpanum character */ /* Scan a non-alphanumeric token */ CHECK_SIZE_TOKEN(3); /* things like "<<=" */ *e_token++ = *buf_ptr; /* if it is only a one-character token, it is * moved here */ *e_token = '\0'; if (++buf_ptr >= buf_end) fill_buffer(); switch (*token) { case '\n': unary_delim = state->last_u_d; state->last_nl = true; /* remember that we just had a newline */ code = (had_eof ? 0 : newline); /* * if data has been exhausted, the newline is a dummy, and we should * return code to stop */ break; case '\'': /* start of quoted character */ case '"': /* start of string */ qchar = *token; do { /* copy the string */ while (1) { /* move one character or [/] */ if (*buf_ptr == '\n') { diag2(1, "Unterminated literal"); goto stop_lit; } CHECK_SIZE_TOKEN(2); *e_token = *buf_ptr++; if (buf_ptr >= buf_end) fill_buffer(); if (*e_token == BACKSLASH) { /* if escape, copy extra char */ if (*buf_ptr == '\n') /* check for escaped newline */ ++line_no; *++e_token = *buf_ptr++; ++e_token; /* we must increment this again because we * copied two chars */ if (buf_ptr >= buf_end) fill_buffer(); } else break; /* we copied one character */ } /* end of while (1) */ } while (*e_token++ != qchar); stop_lit: code = ident; break; case ('('): case ('['): unary_delim = true; code = lparen; break; case (')'): case (']'): code = rparen; break; case '#': unary_delim = state->last_u_d; code = preesc; break; case '?': unary_delim = true; code = question; break; case (':'): code = colon; unary_delim = true; break; case (';'): unary_delim = true; code = semicolon; break; case ('{'): unary_delim = true; /* * if (state->in_or_st) state->block_init = 1; */ /* ? code = state->block_init ? lparen : lbrace; */ code = lbrace; break; case ('}'): unary_delim = true; /* ? code = state->block_init ? rparen : rbrace; */ code = rbrace; break; case 014: /* a form feed */ unary_delim = state->last_u_d; state->last_nl = true; /* remember this so we can set 'state->col_1' * right */ code = form_feed; break; case (','): unary_delim = true; code = comma; break; case '.': unary_delim = false; code = period; break; case '-': case '+': /* check for -, +, --, ++ */ code = (state->last_u_d ? unary_op : binary_op); unary_delim = true; if (*buf_ptr == token[0]) { /* check for doubled character */ *e_token++ = *buf_ptr++; /* buffer overflow will be checked at end of loop */ if (state->last_token == ident || state->last_token == rparen) { code = (state->last_u_d ? unary_op : postop); /* check for following ++ or -- */ unary_delim = false; } } else if (*buf_ptr == '=') /* check for operator += */ *e_token++ = *buf_ptr++; else if (*buf_ptr == '>') { /* check for operator -> */ *e_token++ = *buf_ptr++; if (!opt.pointer_as_binop) { unary_delim = false; code = unary_op; state->want_blank = false; } } break; /* buffer overflow will be checked at end of * switch */ case '=': if (state->in_or_st) state->block_init = 1; if (*buf_ptr == '=') {/* == */ *e_token++ = '='; /* Flip =+ to += */ buf_ptr++; *e_token = 0; } code = binary_op; unary_delim = true; break; /* can drop thru!!! */ case '>': case '<': case '!': /* ops like <, <<, <=, !=, etc */ if (*buf_ptr == '>' || *buf_ptr == '<' || *buf_ptr == '=') { *e_token++ = *buf_ptr; if (++buf_ptr >= buf_end) fill_buffer(); } if (*buf_ptr == '=') *e_token++ = *buf_ptr++; code = (state->last_u_d ? unary_op : binary_op); unary_delim = true; break; case '*': unary_delim = true; if (!state->last_u_d) { if (*buf_ptr == '=') *e_token++ = *buf_ptr++; code = binary_op; break; } while (*buf_ptr == '*' || isspace((unsigned char)*buf_ptr)) { if (*buf_ptr == '*') { CHECK_SIZE_TOKEN(1); *e_token++ = *buf_ptr; } if (++buf_ptr >= buf_end) fill_buffer(); } if (ps.in_decl) { char *tp = buf_ptr; while (isalpha((unsigned char)*tp) || isspace((unsigned char)*tp)) { if (++tp >= buf_end) fill_buffer(); } if (*tp == '(') ps.procname[0] = ' '; } code = unary_op; break; default: if (token[0] == '/' && *buf_ptr == '*') { /* it is start of comment */ *e_token++ = '*'; if (++buf_ptr >= buf_end) fill_buffer(); code = comment; unary_delim = state->last_u_d; break; } while (*(e_token - 1) == *buf_ptr || *buf_ptr == '=') { /* * handle ||, &&, etc, and also things as in int *****i */ CHECK_SIZE_TOKEN(1); *e_token++ = *buf_ptr; if (++buf_ptr >= buf_end) fill_buffer(); } code = (state->last_u_d ? unary_op : binary_op); unary_delim = true; } /* end of switch */ if (buf_ptr >= buf_end) /* check for input buffer empty */ fill_buffer(); state->last_u_d = unary_delim; CHECK_SIZE_TOKEN(1); *e_token = '\0'; /* null terminate the token */ return (code); } /* Initialize constant transition table */ void init_constant_tt(void) { table['-'] = table['+']; table['8'] = table['9']; table['2'] = table['3'] = table['4'] = table['5'] = table['6'] = table['7']; table['A'] = table['C'] = table['D'] = table['c'] = table['d'] = table['a']; table['B'] = table['b']; table['E'] = table['e']; table['U'] = table['u']; table['X'] = table['x']; table['P'] = table['p']; table['F'] = table['f']; } void alloc_typenames(void) { typenames = (const char **)malloc(sizeof(typenames[0]) * (typename_count = 16)); if (typenames == NULL) err(1, NULL); } void add_typename(const char *key) { int comparison; const char *copy; if (typename_top + 1 >= typename_count) { typenames = realloc((void *)typenames, sizeof(typenames[0]) * (typename_count *= 2)); if (typenames == NULL) err(1, NULL); } if (typename_top == -1) typenames[++typename_top] = copy = strdup(key); else if ((comparison = strcmp(key, typenames[typename_top])) >= 0) { /* take advantage of sorted input */ if (comparison == 0) /* remove duplicates */ return; typenames[++typename_top] = copy = strdup(key); } else { int p; for (p = 0; (comparison = strcmp(key, typenames[p])) > 0; p++) /* find place for the new key */; if (comparison == 0) /* remove duplicates */ return; memmove(&typenames[p + 1], &typenames[p], sizeof(typenames[0]) * (++typename_top - p)); typenames[p] = copy = strdup(key); } if (copy == NULL) err(1, NULL); } diff --git a/usr.bin/indent/parse.c b/usr.bin/indent/parse.c index 3d689494b6d9..4434e9b01c16 100644 --- a/usr.bin/indent/parse.c +++ b/usr.bin/indent/parse.c @@ -1,344 +1,341 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1985 Sun Microsystems, Inc. * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * 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 -#endif - #include #include #include #include "indent_globs.h" #include "indent_codes.h" #include "indent.h" /* Globals */ int break_comma; float case_ind; static void reduce(void); void parse(int tk) /* tk: the code for the construct scanned */ { int i; #ifdef debug printf("%2d - %s\n", tk, token); #endif while (ps.p_stack[ps.tos] == ifhead && tk != elselit) { /* true if we have an if without an else */ ps.p_stack[ps.tos] = stmt; /* apply the if(..) stmt ::= stmt * reduction */ reduce(); /* see if this allows any reduction */ } switch (tk) { /* go on and figure out what to do with the * input */ case decl: /* scanned a declaration word */ ps.search_brace = opt.btype_2; /* indicate that following brace should be on same line */ if (ps.p_stack[ps.tos] != decl) { /* only put one declaration * onto stack */ break_comma = true; /* while in declaration, newline should be * forced after comma */ ps.p_stack[++ps.tos] = decl; ps.il[ps.tos] = ps.i_l_follow; if (opt.ljust_decl) {/* only do if we want left justified * declarations */ ps.ind_level = 0; for (i = ps.tos - 1; i > 0; --i) if (ps.p_stack[i] == decl) ++ps.ind_level; /* indentation is number of * declaration levels deep we are */ ps.i_l_follow = ps.ind_level; } } break; case ifstmt: /* scanned if (...) */ if (ps.p_stack[ps.tos] == elsehead && opt.else_if) /* "else if ..." */ /* * Note that the stack pointer here is decremented, effectively * reducing "else if" to "if". This saves a lot of stack space * in case of a long "if-else-if ... else-if" sequence. */ ps.i_l_follow = ps.il[ps.tos--]; /* the rest is the same as for dolit and forstmt */ /* FALLTHROUGH */ case dolit: /* 'do' */ case forstmt: /* for (...) */ ps.p_stack[++ps.tos] = tk; ps.il[ps.tos] = ps.ind_level = ps.i_l_follow; ++ps.i_l_follow; /* subsequent statements should be indented 1 */ ps.search_brace = opt.btype_2; break; case lbrace: /* scanned { */ break_comma = false; /* don't break comma in an initial list */ if (ps.p_stack[ps.tos] == stmt || ps.p_stack[ps.tos] == decl || ps.p_stack[ps.tos] == stmtl) ++ps.i_l_follow; /* it is a random, isolated stmt group or a * declaration */ else { if (s_code == e_code) { /* * only do this if there is nothing on the line */ --ps.ind_level; /* * it is a group as part of a while, for, etc. */ if (ps.p_stack[ps.tos] == swstmt && opt.case_indent >= 1) --ps.ind_level; /* * for a switch, brace should be two levels out from the code */ } } ps.p_stack[++ps.tos] = lbrace; ps.il[ps.tos] = ps.ind_level; ps.p_stack[++ps.tos] = stmt; /* allow null stmt between braces */ ps.il[ps.tos] = ps.i_l_follow; break; case whilestmt: /* scanned while (...) */ if (ps.p_stack[ps.tos] == dohead) { /* it is matched with do stmt */ ps.ind_level = ps.i_l_follow = ps.il[ps.tos]; ps.p_stack[++ps.tos] = whilestmt; ps.il[ps.tos] = ps.ind_level = ps.i_l_follow; } else { /* it is a while loop */ ps.p_stack[++ps.tos] = whilestmt; ps.il[ps.tos] = ps.i_l_follow; ++ps.i_l_follow; ps.search_brace = opt.btype_2; } break; case elselit: /* scanned an else */ if (ps.p_stack[ps.tos] != ifhead) diag2(1, "Unmatched 'else'"); else { ps.ind_level = ps.il[ps.tos]; /* indentation for else should * be same as for if */ ps.i_l_follow = ps.ind_level + 1; /* everything following should * be in 1 level */ ps.p_stack[ps.tos] = elsehead; /* remember if with else */ ps.search_brace = opt.btype_2 | opt.else_if; } break; case rbrace: /* scanned a } */ /* stack should have or */ if (ps.tos > 0 && ps.p_stack[ps.tos - 1] == lbrace) { ps.ind_level = ps.i_l_follow = ps.il[--ps.tos]; ps.p_stack[ps.tos] = stmt; } else diag2(1, "Statement nesting error"); break; case swstmt: /* had switch (...) */ ps.p_stack[++ps.tos] = swstmt; ps.cstk[ps.tos] = case_ind; /* save current case indent level */ ps.il[ps.tos] = ps.i_l_follow; case_ind = ps.i_l_follow + opt.case_indent; /* cases should be one * level down from * switch */ ps.i_l_follow += opt.case_indent + 1; /* statements should be two * levels in */ ps.search_brace = opt.btype_2; break; case semicolon: /* this indicates a simple stmt */ break_comma = false; /* turn off flag to break after commas in a * declaration */ ps.p_stack[++ps.tos] = stmt; ps.il[ps.tos] = ps.ind_level; break; default: /* this is an error */ diag2(1, "Unknown code to parser"); return; } /* end of switch */ if (ps.tos >= STACKSIZE - 1) errx(1, "Parser stack overflow"); reduce(); /* see if any reduction can be done */ #ifdef debug for (i = 1; i <= ps.tos; ++i) printf("(%d %d)", ps.p_stack[i], ps.il[i]); printf("\n"); #endif return; } /* * NAME: reduce * * FUNCTION: Implements the reduce part of the parsing algorithm * * ALGORITHM: The following reductions are done. Reductions are repeated * until no more are possible. * * Old TOS New TOS * * * do "dostmt" * if "ifstmt" * switch * decl * "ifelse" * for * while * "dostmt" while * * On each reduction, ps.i_l_follow (the indentation for the following line) * is set to the indentation level associated with the old TOS. * * PARAMETERS: None * * RETURNS: Nothing * * GLOBALS: ps.cstk ps.i_l_follow = ps.il ps.p_stack = ps.tos = * * CALLS: None * * CALLED BY: parse * * HISTORY: initial coding November 1976 D A Willcox of CAC * */ /*----------------------------------------------*\ | REDUCTION PHASE | \*----------------------------------------------*/ static void reduce(void) { int i; for (;;) { /* keep looping until there is nothing left to * reduce */ switch (ps.p_stack[ps.tos]) { case stmt: switch (ps.p_stack[ps.tos - 1]) { case stmt: case stmtl: /* stmtl stmt or stmt stmt */ ps.p_stack[--ps.tos] = stmtl; break; case dolit: /* */ ps.p_stack[--ps.tos] = dohead; ps.i_l_follow = ps.il[ps.tos]; break; case ifstmt: /* */ ps.p_stack[--ps.tos] = ifhead; for (i = ps.tos - 1; ( ps.p_stack[i] != stmt && ps.p_stack[i] != stmtl && ps.p_stack[i] != lbrace ); --i); ps.i_l_follow = ps.il[i]; /* * for the time being, we will assume that there is no else on * this if, and set the indentation level accordingly. If an * else is scanned, it will be fixed up later */ break; case swstmt: /* */ case_ind = ps.cstk[ps.tos - 1]; /* FALLTHROUGH */ case decl: /* finish of a declaration */ case elsehead: /* < else> */ case forstmt: /* */ case whilestmt: /* */ ps.p_stack[--ps.tos] = stmt; ps.i_l_follow = ps.il[ps.tos]; break; default: /* */ return; } /* end of section for on top of stack */ break; case whilestmt: /* while (...) on top */ if (ps.p_stack[ps.tos - 1] == dohead) { /* it is termination of a do while */ ps.tos -= 2; break; } else return; default: /* anything else on top */ return; } } } diff --git a/usr.bin/indent/pr_comment.c b/usr.bin/indent/pr_comment.c index 5df747892e01..c9de7c2ccfe4 100644 --- a/usr.bin/indent/pr_comment.c +++ b/usr.bin/indent/pr_comment.c @@ -1,350 +1,347 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1985 Sun Microsystems, Inc. * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * 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 -#endif - #include #include #include #include #include #include "indent_globs.h" #include "indent_codes.h" #include "indent.h" /* * NAME: * pr_comment * * FUNCTION: * This routine takes care of scanning and printing comments. * * ALGORITHM: * 1) Decide where the comment should be aligned, and if lines should * be broken. * 2) If lines should not be broken and filled, just copy up to end of * comment. * 3) If lines should be filled, then scan thru input_buffer copying * characters to com_buf. Remember where the last blank, tab, or * newline was. When line is filled, print up to last blank and * continue copying. * * HISTORY: * November 1976 D A Willcox of CAC Initial coding * 12/6/76 D A Willcox of CAC Modification to handle * UNIX-style comments * */ /* * this routine processes comments. It makes an attempt to keep comments from * going over the max line length. If a line is too long, it moves everything * from the last blank to the next comment line. Blanks and tabs from the * beginning of the input line are removed */ void pr_comment(void) { int now_col; /* column we are in now */ int adj_max_col; /* Adjusted max_col for when we decide to * spill comments over the right margin */ char *last_bl; /* points to the last blank in the output * buffer */ char *t_ptr; /* used for moving string */ int break_delim = opt.comment_delimiter_on_blankline; int l_just_saw_decl = ps.just_saw_decl; adj_max_col = opt.max_col; ps.just_saw_decl = 0; last_bl = NULL; /* no blanks found so far */ ps.box_com = false; /* at first, assume that we are not in * a boxed comment or some other * comment that should not be touched */ ++ps.out_coms; /* keep track of number of comments */ /* Figure where to align and how to treat the comment */ if (ps.col_1 && !opt.format_col1_comments) { /* if comment starts in column * 1 it should not be touched */ ps.box_com = true; break_delim = false; ps.com_col = 1; } else { if (*buf_ptr == '-' || *buf_ptr == '*' || (*buf_ptr == '\n' && !opt.format_block_comments)) { ps.box_com = true; /* A comment with a '-' or '*' immediately * after the /+* is assumed to be a boxed * comment. A comment with a newline * immediately after the /+* is assumed to * be a block comment and is treated as a * box comment unless format_block_comments * is nonzero (the default). */ break_delim = false; } if ( /* ps.bl_line && */ (s_lab == e_lab) && (s_code == e_code)) { /* klg: check only if this line is blank */ /* * If this (*and previous lines are*) blank, dont put comment way * out at left */ ps.com_col = (ps.ind_level - opt.unindent_displace) * opt.ind_size + 1; adj_max_col = opt.block_comment_max_col; if (ps.com_col <= 1) ps.com_col = 1 + !opt.format_col1_comments; } else { int target_col; break_delim = false; if (s_code != e_code) target_col = count_spaces(compute_code_target(), s_code); else { target_col = 1; if (s_lab != e_lab) target_col = count_spaces(compute_label_target(), s_lab); } ps.com_col = ps.decl_on_line || ps.ind_level == 0 ? opt.decl_com_ind : opt.com_ind; if (ps.com_col <= target_col) ps.com_col = opt.tabsize * (1 + (target_col - 1) / opt.tabsize) + 1; if (ps.com_col + 24 > adj_max_col) adj_max_col = ps.com_col + 24; } } if (ps.box_com) { /* * Find out how much indentation there was originally, because that * much will have to be ignored by pad_output() in dump_line(). This * is a box comment, so nothing changes -- not even indentation. * * The comment we're about to read usually comes from in_buffer, * unless it has been copied into save_com. */ char *start; start = buf_ptr >= save_com && buf_ptr < save_com + sc_size ? sc_buf : in_buffer; ps.n_comment_delta = 1 - count_spaces_until(1, start, buf_ptr - 2); } else { ps.n_comment_delta = 0; while (*buf_ptr == ' ' || *buf_ptr == '\t') buf_ptr++; } ps.comment_delta = 0; *e_com++ = '/'; /* put '/' followed by '*' into buffer */ *e_com++ = '*'; if (*buf_ptr != ' ' && !ps.box_com) *e_com++ = ' '; /* * Don't put a break delimiter if this is a one-liner that won't wrap. */ if (break_delim) for (t_ptr = buf_ptr; *t_ptr != '\0' && *t_ptr != '\n'; t_ptr++) { if (t_ptr >= buf_end) fill_buffer(); if (t_ptr[0] == '*' && t_ptr[1] == '/') { if (adj_max_col >= count_spaces_until(ps.com_col, buf_ptr, t_ptr + 2)) break_delim = false; break; } } if (break_delim) { char *t = e_com; e_com = s_com + 2; *e_com = 0; if (opt.blanklines_before_blockcomments && ps.last_token != lbrace) prefix_blankline_requested = 1; dump_line(); e_com = s_com = t; if (!ps.box_com && opt.star_comment_cont) *e_com++ = ' ', *e_com++ = '*', *e_com++ = ' '; } /* Start to copy the comment */ while (1) { /* this loop will go until the comment is * copied */ switch (*buf_ptr) { /* this checks for various spcl cases */ case 014: /* check for a form feed */ CHECK_SIZE_COM(3); if (!ps.box_com) { /* in a text comment, break the line here */ ps.use_ff = true; /* fix so dump_line uses a form feed */ dump_line(); last_bl = NULL; if (!ps.box_com && opt.star_comment_cont) *e_com++ = ' ', *e_com++ = '*', *e_com++ = ' '; while (*++buf_ptr == ' ' || *buf_ptr == '\t') ; } else { if (++buf_ptr >= buf_end) fill_buffer(); *e_com++ = 014; } break; case '\n': if (had_eof) { /* check for unexpected eof */ printf("Unterminated comment\n"); dump_line(); return; } last_bl = NULL; CHECK_SIZE_COM(4); if (ps.box_com || ps.last_nl) { /* if this is a boxed comment, * we dont ignore the newline */ if (s_com == e_com) *e_com++ = ' '; if (!ps.box_com && e_com - s_com > 3) { dump_line(); if (opt.star_comment_cont) *e_com++ = ' ', *e_com++ = '*', *e_com++ = ' '; } dump_line(); if (!ps.box_com && opt.star_comment_cont) *e_com++ = ' ', *e_com++ = '*', *e_com++ = ' '; } else { ps.last_nl = 1; if (*(e_com - 1) == ' ' || *(e_com - 1) == '\t') last_bl = e_com - 1; /* * if there was a space at the end of the last line, remember * where it was */ else { /* otherwise, insert one */ last_bl = e_com; *e_com++ = ' '; } } ++line_no; /* keep track of input line number */ if (!ps.box_com) { int nstar = 1; do { /* flush any blanks and/or tabs at start of * next line */ if (++buf_ptr >= buf_end) fill_buffer(); if (*buf_ptr == '*' && --nstar >= 0) { if (++buf_ptr >= buf_end) fill_buffer(); if (*buf_ptr == '/') goto end_of_comment; } } while (*buf_ptr == ' ' || *buf_ptr == '\t'); } else if (++buf_ptr >= buf_end) fill_buffer(); break; /* end of case for newline */ case '*': /* must check for possibility of being at end * of comment */ if (++buf_ptr >= buf_end) /* get to next char after * */ fill_buffer(); CHECK_SIZE_COM(4); if (*buf_ptr == '/') { /* it is the end!!! */ end_of_comment: if (++buf_ptr >= buf_end) fill_buffer(); if (break_delim) { if (e_com > s_com + 3) { dump_line(); } else s_com = e_com; *e_com++ = ' '; } if (e_com[-1] != ' ' && e_com[-1] != '\t' && !ps.box_com) *e_com++ = ' '; /* ensure blank before end */ *e_com++ = '*', *e_com++ = '/', *e_com = '\0'; ps.just_saw_decl = l_just_saw_decl; return; } else /* handle isolated '*' */ *e_com++ = '*'; break; default: /* we have a random char */ now_col = count_spaces_until(ps.com_col, s_com, e_com); do { CHECK_SIZE_COM(1); *e_com = *buf_ptr++; if (buf_ptr >= buf_end) fill_buffer(); if (*e_com == ' ' || *e_com == '\t') last_bl = e_com; /* remember we saw a blank */ ++e_com; now_col++; } while (!memchr("*\n\r\b\t", *buf_ptr, 6) && (now_col <= adj_max_col || !last_bl)); ps.last_nl = false; if (now_col > adj_max_col && !ps.box_com && e_com[-1] > ' ') { /* * the comment is too long, it must be broken up */ if (last_bl == NULL) { dump_line(); if (!ps.box_com && opt.star_comment_cont) *e_com++ = ' ', *e_com++ = '*', *e_com++ = ' '; break; } *e_com = '\0'; e_com = last_bl; dump_line(); if (!ps.box_com && opt.star_comment_cont) *e_com++ = ' ', *e_com++ = '*', *e_com++ = ' '; for (t_ptr = last_bl + 1; *t_ptr == ' ' || *t_ptr == '\t'; t_ptr++) ; last_bl = NULL; /* * t_ptr will be somewhere between e_com (dump_line() reset) * and l_com. So it's safe to copy byte by byte from t_ptr * to e_com without any CHECK_SIZE_COM(). */ while (*t_ptr != '\0') { if (*t_ptr == ' ' || *t_ptr == '\t') last_bl = e_com; *e_com++ = *t_ptr++; } } break; } } } diff --git a/usr.bin/ipcrm/ipcrm.c b/usr.bin/ipcrm/ipcrm.c index 6c2361f056d2..0e0d40837127 100644 --- a/usr.bin/ipcrm/ipcrm.c +++ b/usr.bin/ipcrm/ipcrm.c @@ -1,299 +1,298 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1994 Adam Glass * 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 Adam Glass. * 4. The name of the Author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Adam Glass ``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 Adam Glass BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include #include #define _WANT_SYSVMSG_INTERNALS #include #define _WANT_SYSVSEM_INTERNALS #define _WANT_SEMUN #include #define _WANT_SYSVSHM_INTERNALS #include #include #include #include #include #include #include #include #include "ipc.h" static int signaled; static int errflg; static int rmverbose = 0; static void usage(void) { fprintf(stderr, "usage: ipcrm [-W] [-v[v]]\n" " [-q msqid] [-m shmid] [-s semid]\n" " [-Q msgkey] [-M shmkey] [-S semkey] ...\n"); exit(1); } static int msgrm(key_t key, int id) { if (key == -1 || id == -1) { struct msqid_kernel *kxmsqids; size_t kxmsqids_len; int num; kget(X_MSGINFO, &msginfo, sizeof(msginfo)); kxmsqids_len = sizeof(struct msqid_kernel) * msginfo.msgmni; kxmsqids = malloc(kxmsqids_len); kget(X_MSQIDS, kxmsqids, kxmsqids_len); num = msginfo.msgmni; while (num-- && !signaled) if (kxmsqids[num].u.msg_qbytes != 0) { id = IXSEQ_TO_IPCID(num, kxmsqids[num].u.msg_perm); if (msgctl(id, IPC_RMID, NULL) < 0) { if (rmverbose > 1) warn("msqid(%d): ", id); errflg++; } else if (rmverbose) printf( "Removed %s %d\n", IPC_TO_STRING('Q'), id); } return signaled ? -1 : 0; /* errors maybe handled above */ } if (key) { id = msgget(key, 0); if (id == -1) return -1; } return msgctl(id, IPC_RMID, NULL); } static int shmrm(key_t key, int id) { if (key == -1 || id == -1) { struct shmid_kernel *kxshmids; size_t kxshmids_len; int num; kget(X_SHMINFO, &shminfo, sizeof(shminfo)); kxshmids_len = sizeof(struct shmid_kernel) * shminfo.shmmni; kxshmids = malloc(kxshmids_len); kget(X_SHMSEGS, kxshmids, kxshmids_len); num = shminfo.shmmni; while (num-- && !signaled) if (kxshmids[num].u.shm_perm.mode & 0x0800) { id = IXSEQ_TO_IPCID(num, kxshmids[num].u.shm_perm); if (shmctl(id, IPC_RMID, NULL) < 0) { if (rmverbose > 1) warn("shmid(%d): ", id); errflg++; } else if (rmverbose) printf( "Removed %s %d\n", IPC_TO_STRING('M'), id); } return signaled ? -1 : 0; /* errors maybe handled above */ } if (key) { id = shmget(key, 0, 0); if (id == -1) return -1; } return shmctl(id, IPC_RMID, NULL); } static int semrm(key_t key, int id) { union semun arg; if (key == -1 || id == -1) { struct semid_kernel *kxsema; size_t kxsema_len; int num; kget(X_SEMINFO, &seminfo, sizeof(seminfo)); kxsema_len = sizeof(struct semid_kernel) * seminfo.semmni; kxsema = malloc(kxsema_len); kget(X_SEMA, kxsema, kxsema_len); num = seminfo.semmni; while (num-- && !signaled) if ((kxsema[num].u.sem_perm.mode & SEM_ALLOC) != 0) { id = IXSEQ_TO_IPCID(num, kxsema[num].u.sem_perm); if (semctl(id, 0, IPC_RMID, NULL) < 0) { if (rmverbose > 1) warn("semid(%d): ", id); errflg++; } else if (rmverbose) printf( "Removed %s %d\n", IPC_TO_STRING('S'), id); } return signaled ? -1 : 0; /* errors maybe handled above */ } if (key) { id = semget(key, 0, 0); if (id == -1) return -1; } return semctl(id, 0, IPC_RMID, arg); } static void not_configured(int signo __unused) { signaled++; } int main(int argc, char *argv[]) { int c, result, target_id; key_t target_key; while ((c = getopt(argc, argv, "q:m:s:Q:M:S:vWy")) != -1) { signaled = 0; switch (c) { case 'v': rmverbose++; break; case 'y': use_sysctl = 0; break; } } optind = 1; errflg = 0; signal(SIGSYS, not_configured); while ((c = getopt(argc, argv, "q:m:s:Q:M:S:vWy")) != -1) { signaled = 0; switch (c) { case 'q': case 'm': case 's': target_id = atoi(optarg); if (c == 'q') result = msgrm(0, target_id); else if (c == 'm') result = shmrm(0, target_id); else result = semrm(0, target_id); if (result < 0) { errflg++; if (!signaled) warn("%sid(%d): ", IPC_TO_STR(toupper(c)), target_id); else warnx( "%ss are not configured " "in the running kernel", IPC_TO_STRING(toupper(c))); } break; case 'Q': case 'M': case 'S': target_key = atol(optarg); if (target_key == IPC_PRIVATE) { warnx("can't remove private %ss", IPC_TO_STRING(c)); continue; } if (c == 'Q') result = msgrm(target_key, 0); else if (c == 'M') result = shmrm(target_key, 0); else result = semrm(target_key, 0); if (result < 0) { errflg++; if (!signaled) warn("%ss(%ld): ", IPC_TO_STR(c), target_key); else warnx("%ss are not configured " "in the running kernel", IPC_TO_STRING(c)); } break; case 'v': case 'y': /* Handled in other getopt() loop */ break; case 'W': msgrm(-1, 0); shmrm(-1, 0); semrm(-1, 0); break; case ':': fprintf(stderr, "option -%c requires an argument\n", optopt); usage(); case '?': fprintf(stderr, "unrecognized option: -%c\n", optopt); usage(); } } if (optind != argc) { fprintf(stderr, "unknown argument: %s\n", argv[optind]); usage(); } exit(errflg); } diff --git a/usr.bin/ipcs/ipc.c b/usr.bin/ipcs/ipc.c index 0729f27e5945..0d9cccf20421 100644 --- a/usr.bin/ipcs/ipc.c +++ b/usr.bin/ipcs/ipc.c @@ -1,205 +1,204 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1994 SigmaSoft, Th. Lockert * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The split of ipcs.c into ipcs.c and ipc.c to accommodate the * changes in ipcrm.c was done by Edwin Groothuis */ -#include #include #include #define _WANT_SYSVMSG_INTERNALS #include #define _WANT_SYSVSEM_INTERNALS #include #define _WANT_SYSVSHM_INTERNALS #include #include #include #include #include #include #include "ipc.h" int use_sysctl = 1; struct semid_kernel *sema; struct seminfo seminfo; struct msginfo msginfo; struct msqid_kernel *msqids; struct shminfo shminfo; struct shmid_kernel *shmsegs; struct nlist symbols[] = { { .n_name = "sema" }, { .n_name = "seminfo" }, { .n_name = "msginfo" }, { .n_name = "msqids" }, { .n_name = "shminfo" }, { .n_name = "shmsegs" }, { .n_name = NULL } }; #define SHMINFO_XVEC X(shmmax, sizeof(u_long)) \ X(shmmin, sizeof(u_long)) \ X(shmmni, sizeof(u_long)) \ X(shmseg, sizeof(u_long)) \ X(shmall, sizeof(u_long)) #define SEMINFO_XVEC X(semmni, sizeof(int)) \ X(semmns, sizeof(int)) \ X(semmnu, sizeof(int)) \ X(semmsl, sizeof(int)) \ X(semopm, sizeof(int)) \ X(semume, sizeof(int)) \ X(semusz, sizeof(int)) \ X(semvmx, sizeof(int)) \ X(semaem, sizeof(int)) #define MSGINFO_XVEC X(msgmax, sizeof(int)) \ X(msgmni, sizeof(int)) \ X(msgmnb, sizeof(int)) \ X(msgtql, sizeof(int)) \ X(msgssz, sizeof(int)) \ X(msgseg, sizeof(int)) #define X(a, b) { "kern.ipc." #a, offsetof(TYPEC, a), (b) }, #define TYPEC struct shminfo static struct scgs_vector shminfo_scgsv[] = { SHMINFO_XVEC { .sysctl=NULL } }; #undef TYPEC #define TYPEC struct seminfo static struct scgs_vector seminfo_scgsv[] = { SEMINFO_XVEC { .sysctl=NULL } }; #undef TYPEC #define TYPEC struct msginfo static struct scgs_vector msginfo_scgsv[] = { MSGINFO_XVEC { .sysctl=NULL } }; #undef TYPEC #undef X kvm_t *kd; void sysctlgatherstruct(void *addr, size_t size, struct scgs_vector *vecarr) { struct scgs_vector *xp; size_t tsiz; int rv; for (xp = vecarr; xp->sysctl != NULL; xp++) { assert(xp->offset <= size); tsiz = xp->size; rv = sysctlbyname(xp->sysctl, (char *)addr + xp->offset, &tsiz, NULL, 0); if (rv == -1) err(1, "sysctlbyname: %s", xp->sysctl); if (tsiz != xp->size) errx(1, "%s size mismatch (expected %zu, got %zu)", xp->sysctl, xp->size, tsiz); } } void kget(int idx, void *addr, size_t size) { const char *symn; /* symbol name */ size_t tsiz; int rv; unsigned long kaddr; const char *sym2sysctl[] = { /* symbol to sysctl name table */ "kern.ipc.sema", "kern.ipc.seminfo", "kern.ipc.msginfo", "kern.ipc.msqids", "kern.ipc.shminfo", "kern.ipc.shmsegs" }; assert((unsigned)idx <= sizeof(sym2sysctl) / sizeof(*sym2sysctl)); if (!use_sysctl) { symn = symbols[idx].n_name; if (*symn == '_') symn++; if (symbols[idx].n_type == 0 || symbols[idx].n_value == 0) errx(1, "symbol %s undefined", symn); /* * For some symbols, the value we retrieve is * actually a pointer; since we want the actual value, * we have to manually dereference it. */ switch (idx) { case X_MSQIDS: tsiz = sizeof(msqids); rv = kvm_read(kd, symbols[idx].n_value, &msqids, tsiz); kaddr = (u_long)msqids; break; case X_SHMSEGS: tsiz = sizeof(shmsegs); rv = kvm_read(kd, symbols[idx].n_value, &shmsegs, tsiz); kaddr = (u_long)shmsegs; break; case X_SEMA: tsiz = sizeof(sema); rv = kvm_read(kd, symbols[idx].n_value, &sema, tsiz); kaddr = (u_long)sema; break; default: rv = tsiz = 0; kaddr = symbols[idx].n_value; break; } if ((unsigned)rv != tsiz) errx(1, "%s: %s", symn, kvm_geterr(kd)); if ((unsigned)kvm_read(kd, kaddr, addr, size) != size) errx(1, "%s: %s", symn, kvm_geterr(kd)); } else { switch (idx) { case X_SHMINFO: sysctlgatherstruct(addr, size, shminfo_scgsv); break; case X_SEMINFO: sysctlgatherstruct(addr, size, seminfo_scgsv); break; case X_MSGINFO: sysctlgatherstruct(addr, size, msginfo_scgsv); break; default: tsiz = size; rv = sysctlbyname(sym2sysctl[idx], addr, &tsiz, NULL, 0); if (rv == -1) err(1, "sysctlbyname: %s", sym2sysctl[idx]); if (tsiz != size) errx(1, "%s size mismatch " "(expected %zu, got %zu)", sym2sysctl[idx], size, tsiz); break; } } } diff --git a/usr.bin/ipcs/ipcs.c b/usr.bin/ipcs/ipcs.c index bcb4f1953fa1..99e2c8f09218 100644 --- a/usr.bin/ipcs/ipcs.c +++ b/usr.bin/ipcs/ipcs.c @@ -1,556 +1,555 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1994 SigmaSoft, Th. Lockert * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #define _WANT_SYSVMSG_INTERNALS #include #define _WANT_SYSVSEM_INTERNALS #include #define _WANT_SYSVSHM_INTERNALS #include #include #include #include #include #include #include #include #include #include #include #include "ipc.h" char *fmt_perm(u_short); void cvt_time(time_t, char *); void usage(void); uid_t user2uid(char *username); void print_kmsqtotal(struct msginfo msginfo); void print_kmsqheader(int option); void print_kmsqptr(int i, int option, struct msqid_kernel *kmsqptr); void print_kshmtotal(struct shminfo shminfo); void print_kshmheader(int option); void print_kshmptr(int i, int option, struct shmid_kernel *kshmptr); void print_ksemtotal(struct seminfo seminfo); void print_ksemheader(int option); void print_ksemptr(int i, int option, struct semid_kernel *ksemaptr); char * fmt_perm(u_short mode) { static char buffer[100]; buffer[0] = '-'; buffer[1] = '-'; buffer[2] = ((mode & 0400) ? 'r' : '-'); buffer[3] = ((mode & 0200) ? 'w' : '-'); buffer[4] = ((mode & 0100) ? 'a' : '-'); buffer[5] = ((mode & 0040) ? 'r' : '-'); buffer[6] = ((mode & 0020) ? 'w' : '-'); buffer[7] = ((mode & 0010) ? 'a' : '-'); buffer[8] = ((mode & 0004) ? 'r' : '-'); buffer[9] = ((mode & 0002) ? 'w' : '-'); buffer[10] = ((mode & 0001) ? 'a' : '-'); buffer[11] = '\0'; return (&buffer[0]); } void cvt_time(time_t t, char *buf) { struct tm *tm; if (t == 0) { strcpy(buf, "no-entry"); } else { tm = localtime(&t); sprintf(buf, "%2d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); } } #define BIGGEST 1 #define CREATOR 2 #define OUTSTANDING 4 #define PID 8 #define TIME 16 int main(int argc, char *argv[]) { int display = SHMINFO | MSGINFO | SEMINFO; int option = 0; char *core = NULL, *user = NULL, *namelist = NULL; char kvmoferr[_POSIX2_LINE_MAX]; /* Error buf for kvm_openfiles. */ int i; u_long shmidx; uid_t uid = 0; while ((i = getopt(argc, argv, "MmQqSsabC:cN:optTu:y")) != -1) switch (i) { case 'a': option |= BIGGEST | CREATOR | OUTSTANDING | PID | TIME; break; case 'b': option |= BIGGEST; break; case 'C': core = optarg; break; case 'c': option |= CREATOR; break; case 'M': display = SHMTOTAL; break; case 'm': display = SHMINFO; break; case 'N': namelist = optarg; break; case 'o': option |= OUTSTANDING; break; case 'p': option |= PID; break; case 'Q': display = MSGTOTAL; break; case 'q': display = MSGINFO; break; case 'S': display = SEMTOTAL; break; case 's': display = SEMINFO; break; case 'T': display = SHMTOTAL | MSGTOTAL | SEMTOTAL; break; case 't': option |= TIME; break; case 'u': user = optarg; uid = user2uid(user); break; case 'y': use_sysctl = 0; break; default: usage(); } /* * If paths to the exec file or core file were specified, we * aren't operating on the running kernel, so we can't use * sysctl. */ if (namelist != NULL || core != NULL) use_sysctl = 0; if (!use_sysctl) { kd = kvm_openfiles(namelist, core, NULL, O_RDONLY, kvmoferr); if (kd == NULL) errx(1, "kvm_openfiles: %s", kvmoferr); switch (kvm_nlist(kd, symbols)) { case 0: break; case -1: errx(1, "unable to read kernel symbol table"); default: break; } } kget(X_MSGINFO, &msginfo, sizeof(msginfo)); if (display & (MSGINFO | MSGTOTAL)) { if (display & MSGTOTAL) print_kmsqtotal(msginfo); if (display & MSGINFO) { struct msqid_kernel *kxmsqids; size_t kxmsqids_len; kxmsqids_len = sizeof(struct msqid_kernel) * msginfo.msgmni; kxmsqids = malloc(kxmsqids_len); kget(X_MSQIDS, kxmsqids, kxmsqids_len); print_kmsqheader(option); for (i = 0; i < msginfo.msgmni; i += 1) { if (kxmsqids[i].u.msg_qbytes != 0) { if (user && uid != kxmsqids[i].u.msg_perm.uid) continue; print_kmsqptr(i, option, &kxmsqids[i]); } } printf("\n"); } } kget(X_SHMINFO, &shminfo, sizeof(shminfo)); if (display & (SHMINFO | SHMTOTAL)) { if (display & SHMTOTAL) print_kshmtotal(shminfo); if (display & SHMINFO) { struct shmid_kernel *kxshmids; size_t kxshmids_len; kxshmids_len = sizeof(struct shmid_kernel) * shminfo.shmmni; kxshmids = malloc(kxshmids_len); kget(X_SHMSEGS, kxshmids, kxshmids_len); print_kshmheader(option); for (shmidx = 0; shmidx < shminfo.shmmni; shmidx += 1) { if (kxshmids[shmidx].u.shm_perm.mode & 0x0800) { if (user && uid != kxshmids[shmidx].u.shm_perm.uid) continue; print_kshmptr(shmidx, option, &kxshmids[shmidx]); } } printf("\n"); } } kget(X_SEMINFO, &seminfo, sizeof(seminfo)); if (display & (SEMINFO | SEMTOTAL)) { struct semid_kernel *kxsema; size_t kxsema_len; if (display & SEMTOTAL) print_ksemtotal(seminfo); if (display & SEMINFO) { kxsema_len = sizeof(struct semid_kernel) * seminfo.semmni; kxsema = malloc(kxsema_len); kget(X_SEMA, kxsema, kxsema_len); print_ksemheader(option); for (i = 0; i < seminfo.semmni; i += 1) { if ((kxsema[i].u.sem_perm.mode & SEM_ALLOC) != 0) { if (user && uid != kxsema[i].u.sem_perm.uid) continue; print_ksemptr(i, option, &kxsema[i]); } } printf("\n"); } } if (!use_sysctl) kvm_close(kd); exit(0); } void print_kmsqtotal(struct msginfo local_msginfo) { printf("msginfo:\n"); printf("\tmsgmax: %12d\t(max characters in a message)\n", local_msginfo.msgmax); printf("\tmsgmni: %12d\t(# of message queues)\n", local_msginfo.msgmni); printf("\tmsgmnb: %12d\t(max characters in a message queue)\n", local_msginfo.msgmnb); printf("\tmsgtql: %12d\t(max # of messages in system)\n", local_msginfo.msgtql); printf("\tmsgssz: %12d\t(size of a message segment)\n", local_msginfo.msgssz); printf("\tmsgseg: %12d\t(# of message segments in system)\n\n", local_msginfo.msgseg); } void print_kmsqheader(int option) { printf("Message Queues:\n"); printf("T %12s %12s %-11s %-8s %-8s", "ID", "KEY", "MODE", "OWNER", "GROUP"); if (option & CREATOR) printf(" %-8s %-8s", "CREATOR", "CGROUP"); if (option & OUTSTANDING) printf(" %20s %20s", "CBYTES", "QNUM"); if (option & BIGGEST) printf(" %20s", "QBYTES"); if (option & PID) printf(" %12s %12s", "LSPID", "LRPID"); if (option & TIME) printf(" %-8s %-8s %-8s", "STIME", "RTIME", "CTIME"); printf("\n"); } void print_kmsqptr(int i, int option, struct msqid_kernel *kmsqptr) { char stime_buf[100], rtime_buf[100], ctime_buf[100]; cvt_time(kmsqptr->u.msg_stime, stime_buf); cvt_time(kmsqptr->u.msg_rtime, rtime_buf); cvt_time(kmsqptr->u.msg_ctime, ctime_buf); printf("q %12d %12d %s %-8s %-8s", IXSEQ_TO_IPCID(i, kmsqptr->u.msg_perm), (int)kmsqptr->u.msg_perm.key, fmt_perm(kmsqptr->u.msg_perm.mode), user_from_uid(kmsqptr->u.msg_perm.uid, 0), group_from_gid(kmsqptr->u.msg_perm.gid, 0)); if (option & CREATOR) printf(" %-8s %-8s", user_from_uid(kmsqptr->u.msg_perm.cuid, 0), group_from_gid(kmsqptr->u.msg_perm.cgid, 0)); if (option & OUTSTANDING) printf(" %12lu %12lu", kmsqptr->u.msg_cbytes, kmsqptr->u.msg_qnum); if (option & BIGGEST) printf(" %20lu", kmsqptr->u.msg_qbytes); if (option & PID) printf(" %12d %12d", kmsqptr->u.msg_lspid, kmsqptr->u.msg_lrpid); if (option & TIME) printf(" %s %s %s", stime_buf, rtime_buf, ctime_buf); printf("\n"); } void print_kshmtotal(struct shminfo local_shminfo) { printf("shminfo:\n"); printf("\tshmmax: %12lu\t(max shared memory segment size)\n", local_shminfo.shmmax); printf("\tshmmin: %12lu\t(min shared memory segment size)\n", local_shminfo.shmmin); printf("\tshmmni: %12lu\t(max number of shared memory identifiers)\n", local_shminfo.shmmni); printf("\tshmseg: %12lu\t(max shared memory segments per process)\n", local_shminfo.shmseg); printf("\tshmall: %12lu\t(max amount of shared memory in pages)\n\n", local_shminfo.shmall); } void print_kshmheader(int option) { printf("Shared Memory:\n"); printf("T %12s %12s %-11s %-8s %-8s", "ID", "KEY", "MODE", "OWNER", "GROUP"); if (option & CREATOR) printf(" %-8s %-8s", "CREATOR", "CGROUP"); if (option & OUTSTANDING) printf(" %12s", "NATTCH"); if (option & BIGGEST) printf(" %12s", "SEGSZ"); if (option & PID) printf(" %12s %12s", "CPID", "LPID"); if (option & TIME) printf(" %-8s %-8s %-8s", "ATIME", "DTIME", "CTIME"); printf("\n"); } void print_kshmptr(int i, int option, struct shmid_kernel *kshmptr) { char atime_buf[100], dtime_buf[100], ctime_buf[100]; cvt_time(kshmptr->u.shm_atime, atime_buf); cvt_time(kshmptr->u.shm_dtime, dtime_buf); cvt_time(kshmptr->u.shm_ctime, ctime_buf); printf("m %12d %12d %s %-8s %-8s", IXSEQ_TO_IPCID(i, kshmptr->u.shm_perm), (int)kshmptr->u.shm_perm.key, fmt_perm(kshmptr->u.shm_perm.mode), user_from_uid(kshmptr->u.shm_perm.uid, 0), group_from_gid(kshmptr->u.shm_perm.gid, 0)); if (option & CREATOR) printf(" %-8s %-8s", user_from_uid(kshmptr->u.shm_perm.cuid, 0), group_from_gid(kshmptr->u.shm_perm.cgid, 0)); if (option & OUTSTANDING) printf(" %12d", kshmptr->u.shm_nattch); if (option & BIGGEST) printf(" %12zu", kshmptr->u.shm_segsz); if (option & PID) printf(" %12d %12d", kshmptr->u.shm_cpid, kshmptr->u.shm_lpid); if (option & TIME) printf(" %s %s %s", atime_buf, dtime_buf, ctime_buf); printf("\n"); } void print_ksemtotal(struct seminfo local_seminfo) { printf("seminfo:\n"); printf("\tsemmni: %12d\t(# of semaphore identifiers)\n", local_seminfo.semmni); printf("\tsemmns: %12d\t(# of semaphores in system)\n", local_seminfo.semmns); printf("\tsemmnu: %12d\t(# of undo structures in system)\n", local_seminfo.semmnu); printf("\tsemmsl: %12d\t(max # of semaphores per id)\n", local_seminfo.semmsl); printf("\tsemopm: %12d\t(max # of operations per semop call)\n", local_seminfo.semopm); printf("\tsemume: %12d\t(max # of undo entries per process)\n", local_seminfo.semume); printf("\tsemusz: %12d\t(size in bytes of undo structure)\n", local_seminfo.semusz); printf("\tsemvmx: %12d\t(semaphore maximum value)\n", local_seminfo.semvmx); printf("\tsemaem: %12d\t(adjust on exit max value)\n\n", local_seminfo.semaem); } void print_ksemheader(int option) { printf("Semaphores:\n"); printf("T %12s %12s %-11s %-8s %-8s", "ID", "KEY", "MODE", "OWNER", "GROUP"); if (option & CREATOR) printf(" %-8s %-8s", "CREATOR", "CGROUP"); if (option & BIGGEST) printf(" %12s", "NSEMS"); if (option & TIME) printf(" %-8s %-8s", "OTIME", "CTIME"); printf("\n"); } void print_ksemptr(int i, int option, struct semid_kernel *ksemaptr) { char ctime_buf[100], otime_buf[100]; cvt_time(ksemaptr->u.sem_otime, otime_buf); cvt_time(ksemaptr->u.sem_ctime, ctime_buf); printf("s %12d %12d %s %-8s %-8s", IXSEQ_TO_IPCID(i, ksemaptr->u.sem_perm), (int)ksemaptr->u.sem_perm.key, fmt_perm(ksemaptr->u.sem_perm.mode), user_from_uid(ksemaptr->u.sem_perm.uid, 0), group_from_gid(ksemaptr->u.sem_perm.gid, 0)); if (option & CREATOR) printf(" %-8s %-8s", user_from_uid(ksemaptr->u.sem_perm.cuid, 0), group_from_gid(ksemaptr->u.sem_perm.cgid, 0)); if (option & BIGGEST) printf(" %12d", ksemaptr->u.sem_nsems); if (option & TIME) printf(" %s %s", otime_buf, ctime_buf); printf("\n"); } uid_t user2uid(char *username) { struct passwd *pwd; uid_t uid; char *r; uid = strtoul(username, &r, 0); if (!*r && r != username) return (uid); if ((pwd = getpwnam(username)) == NULL) errx(1, "getpwnam failed: No such user"); endpwent(); return (pwd->pw_uid); } void usage(void) { fprintf(stderr, "usage: " "ipcs [-abcmopqstyMQST] [-C corefile] [-N namelist] [-u user]\n"); exit(1); } diff --git a/usr.bin/kdump/linux.c b/usr.bin/kdump/linux.c index 4b115f051b44..a52fbb2ba498 100644 --- a/usr.bin/kdump/linux.c +++ b/usr.bin/kdump/linux.c @@ -1,526 +1,525 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Dmitry Chagin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include "kdump.h" #ifdef __amd64__ #include #include #elif __aarch64__ #include #elif __i386__ #include #endif #include #include static void print_linux_signal(int signo) { const char *signame; signame = sysdecode_linux_signal(signo); if (signame != NULL) printf("%s", signame); else printf("SIG %d", signo); } void ktrsyscall_linux(struct ktr_syscall *ktr, register_t **resip, int *resnarg, char *resc) { int narg = ktr->ktr_narg; register_t *ip, *first; int quad_align, quad_slots; char c; ip = first = &ktr->ktr_args[0]; c = *resc; quad_align = 0; quad_slots = 1; switch (ktr->ktr_code) { case LINUX_SYS_linux_faccessat: case LINUX_SYS_linux_fchmodat: case LINUX_SYS_linux_fchownat: #ifdef LINUX_SYS_linux_newfstatat case LINUX_SYS_linux_newfstatat: #endif #ifdef LINUX_SYS_linux_fstatat64 case LINUX_SYS_linux_fstatat64: #endif #ifdef LINUX_SYS_linux_futimesat case LINUX_SYS_linux_futimesat: #endif case LINUX_SYS_linux_linkat: case LINUX_SYS_linux_mkdirat: case LINUX_SYS_linux_mknodat: case LINUX_SYS_linux_openat: case LINUX_SYS_linux_readlinkat: case LINUX_SYS_linux_renameat: case LINUX_SYS_linux_unlinkat: case LINUX_SYS_linux_utimensat: putchar('('); print_integer_arg_valid(sysdecode_atfd, *ip); c = ','; ip++; narg--; break; } switch (ktr->ktr_code) { #ifdef LINUX_SYS_linux_access case LINUX_SYS_linux_access: #endif case LINUX_SYS_linux_faccessat: print_number(ip, narg, c); putchar(','); print_mask_arg(sysdecode_access_mode, *ip); ip++; narg--; break; #ifdef LINUX_SYS_linux_chmod case LINUX_SYS_linux_chmod: #endif case LINUX_SYS_linux_fchmodat: print_number(ip, narg, c); putchar(','); decode_filemode(*ip); ip++; narg--; break; case LINUX_SYS_linux_mknodat: print_number(ip, narg, c); putchar(','); decode_filemode(*ip); ip++; narg--; break; #ifdef LINUX_SYS_linux_mkdir case LINUX_SYS_linux_mkdir: #endif case LINUX_SYS_linux_mkdirat: print_number(ip, narg, c); putchar(','); decode_filemode(*ip); ip++; narg--; break; case LINUX_SYS_linux_linkat: case LINUX_SYS_linux_renameat: case LINUX_SYS_linux_symlinkat: print_number(ip, narg, c); putchar(','); print_integer_arg_valid(sysdecode_atfd, *ip); ip++; narg--; print_number(ip, narg, c); break; case LINUX_SYS_linux_fchownat: print_number(ip, narg, c); print_number(ip, narg, c); print_number(ip, narg, c); break; #ifdef LINUX_SYS_linux_newfstatat case LINUX_SYS_linux_newfstatat: #endif #ifdef LINUX_SYS_linux_fstatat64 case LINUX_SYS_linux_fstatat64: #endif case LINUX_SYS_linux_utimensat: print_number(ip, narg, c); print_number(ip, narg, c); break; case LINUX_SYS_linux_unlinkat: print_number(ip, narg, c); break; case LINUX_SYS_linux_clock_gettime: case LINUX_SYS_linux_clock_settime: case LINUX_SYS_linux_clock_getres: case LINUX_SYS_linux_timer_create: putchar('('); sysdecode_linux_clockid(stdout, *ip); c = ','; ip++; narg--; break; case LINUX_SYS_linux_clock_nanosleep: putchar('('); sysdecode_linux_clockid(stdout, *ip); putchar(','); ip++; narg--; print_mask_arg0(sysdecode_linux_clock_flags, *ip); c = ','; ip++; narg--; break; case LINUX_SYS_linux_clone: putchar('('); print_mask_arg(sysdecode_linux_clone_flags, *ip); ip++; narg--; c = ','; break; case LINUX_SYS_linux_kill: case LINUX_SYS_linux_tkill: case LINUX_SYS_linux_rt_sigqueueinfo: print_decimal_number(ip, narg, c); putchar(','); print_linux_signal(*ip); ip++; narg--; break; case LINUX_SYS_linux_tgkill: case LINUX_SYS_linux_rt_tgsigqueueinfo: print_decimal_number(ip, narg, c); print_decimal_number(ip, narg, c); putchar(','); print_linux_signal(*ip); ip++; narg--; break; #ifdef LINUX_SYS_linux_open case LINUX_SYS_linux_open: #endif case LINUX_SYS_linux_openat: print_number(ip, narg, c); putchar(','); print_mask_arg(sysdecode_linux_open_flags, ip[0]); if ((ip[0] & LINUX_O_CREAT) == LINUX_O_CREAT) { putchar(','); decode_filemode(ip[1]); } ip += 2; narg -= 2; break; case LINUX_SYS_linux_rt_sigaction: putchar('('); print_linux_signal(*ip); ip++; narg--; c = ','; break; case LINUX_SYS_linux_ftruncate: case LINUX_SYS_linux_truncate: print_number(ip, narg, c); print_number64(first, ip, narg, c); break; case LINUX_SYS_linux_getitimer: case LINUX_SYS_linux_setitimer: putchar('('); print_integer_arg(sysdecode_itimer, *ip); ip++; narg--; c = ','; break; case LINUX_SYS_linux_rt_sigprocmask: #ifdef LINUX_SYS_linux_sigprocmask case LINUX_SYS_linux_sigprocmask: #endif putchar('('); print_integer_arg(sysdecode_linux_sigprocmask_how, *ip); ip++; narg--; c = ','; break; } switch (ktr->ktr_code) { case LINUX_SYS_linux_fchownat: case LINUX_SYS_linux_faccessat: case LINUX_SYS_linux_fchmodat: #ifdef LINUX_SYS_linux_newfstatat case LINUX_SYS_linux_newfstatat: #endif #ifdef LINUX_SYS_linux_fstatat64 case LINUX_SYS_linux_fstatat64: #endif case LINUX_SYS_linux_linkat: case LINUX_SYS_linux_unlinkat: case LINUX_SYS_linux_utimensat: putchar(','); print_mask_arg0(sysdecode_linux_atflags, *ip); ip++; narg--; break; } *resc = c; *resip = ip; *resnarg = narg; } #if defined(__amd64__) void ktrsyscall_linux32(struct ktr_syscall *ktr, register_t **resip, int *resnarg, char *resc) { int narg = ktr->ktr_narg; register_t *ip, *first; int quad_align, quad_slots; char c; ip = first = &ktr->ktr_args[0]; c = *resc; quad_align = 0; quad_slots = 2; switch (ktr->ktr_code) { case LINUX32_SYS_linux_faccessat: case LINUX32_SYS_linux_fchmodat: case LINUX32_SYS_linux_fchownat: case LINUX32_SYS_linux_fstatat64: case LINUX32_SYS_linux_futimesat: case LINUX32_SYS_linux_linkat: case LINUX32_SYS_linux_mkdirat: case LINUX32_SYS_linux_mknodat: case LINUX32_SYS_linux_openat: case LINUX32_SYS_linux_readlinkat: case LINUX32_SYS_linux_renameat: case LINUX32_SYS_linux_unlinkat: case LINUX32_SYS_linux_utimensat: putchar('('); print_integer_arg_valid(sysdecode_atfd, *ip); c = ','; ip++; narg--; break; } switch (ktr->ktr_code) { case LINUX32_SYS_linux_access: case LINUX32_SYS_linux_faccessat: print_number(ip, narg, c); putchar(','); print_mask_arg(sysdecode_access_mode, *ip); ip++; narg--; break; case LINUX32_SYS_linux_chmod: case LINUX32_SYS_fchmod: case LINUX32_SYS_linux_fchmodat: print_number(ip, narg, c); putchar(','); decode_filemode(*ip); ip++; narg--; break; case LINUX32_SYS_linux_mknodat: print_number(ip, narg, c); putchar(','); decode_filemode(*ip); ip++; narg--; break; case LINUX32_SYS_linux_mkdir: case LINUX32_SYS_linux_mkdirat: print_number(ip, narg, c); putchar(','); decode_filemode(*ip); ip++; narg--; break; case LINUX32_SYS_linux_linkat: case LINUX32_SYS_linux_renameat: case LINUX32_SYS_linux_symlinkat: print_number(ip, narg, c); putchar(','); print_integer_arg_valid(sysdecode_atfd, *ip); ip++; narg--; print_number(ip, narg, c); break; case LINUX32_SYS_linux_fchownat: print_number(ip, narg, c); print_number(ip, narg, c); print_number(ip, narg, c); break; case LINUX32_SYS_linux_fstatat64: case LINUX32_SYS_linux_utimensat: print_number(ip, narg, c); print_number(ip, narg, c); break; case LINUX32_SYS_linux_unlinkat: print_number(ip, narg, c); break; case LINUX32_SYS_linux_clock_gettime: case LINUX32_SYS_linux_clock_settime: case LINUX32_SYS_linux_clock_getres: case LINUX32_SYS_linux_timer_create: case LINUX32_SYS_linux_clock_gettime64: case LINUX32_SYS_linux_clock_settime64: case LINUX32_SYS_linux_clock_getres_time64: putchar('('); sysdecode_linux_clockid(stdout, *ip); c = ','; ip++; narg--; break; case LINUX32_SYS_linux_clock_nanosleep: putchar('('); sysdecode_linux_clockid(stdout, *ip); putchar(','); ip++; narg--; print_mask_arg0(sysdecode_linux_clock_flags, *ip); c = ','; ip++; narg--; break; case LINUX32_SYS_linux_clone: putchar('('); print_mask_arg(sysdecode_linux_clone_flags, *ip); ip++; narg--; c = ','; break; case LINUX32_SYS_linux_kill: case LINUX32_SYS_linux_tkill: case LINUX32_SYS_linux_rt_sigqueueinfo: print_decimal_number(ip, narg, c); putchar(','); print_linux_signal(*ip); ip++; narg--; break; case LINUX32_SYS_linux_tgkill: case LINUX32_SYS_linux_rt_tgsigqueueinfo: print_decimal_number(ip, narg, c); print_decimal_number(ip, narg, c); putchar(','); print_linux_signal(*ip); ip++; narg--; break; case LINUX32_SYS_linux_open: case LINUX32_SYS_linux_openat: print_number(ip, narg, c); putchar(','); print_mask_arg(sysdecode_linux_open_flags, ip[0]); if ((ip[0] & LINUX_O_CREAT) == LINUX_O_CREAT) { putchar(','); decode_filemode(ip[1]); } ip += 2; narg -= 2; break; case LINUX32_SYS_linux_signal: case LINUX32_SYS_linux_sigaction: case LINUX32_SYS_linux_rt_sigaction: putchar('('); print_linux_signal(*ip); ip++; narg--; c = ','; break; case LINUX32_SYS_linux_ftruncate: case LINUX32_SYS_linux_truncate: print_number(ip, narg, c); print_number64(first, ip, narg, c); break; case LINUX32_SYS_linux_getitimer: case LINUX32_SYS_linux_setitimer: putchar('('); print_integer_arg(sysdecode_itimer, *ip); ip++; narg--; c = ','; break; case LINUX32_SYS_linux_rt_sigprocmask: case LINUX32_SYS_linux_sigprocmask: putchar('('); print_integer_arg(sysdecode_linux_sigprocmask_how, *ip); ip++; narg--; c = ','; break; } switch (ktr->ktr_code) { case LINUX32_SYS_linux_fchownat: case LINUX32_SYS_linux_faccessat: case LINUX32_SYS_linux_fchmodat: case LINUX32_SYS_linux_fstatat64: case LINUX32_SYS_linux_linkat: case LINUX32_SYS_linux_unlinkat: case LINUX32_SYS_linux_utimensat: putchar(','); print_mask_arg0(sysdecode_linux_atflags, *ip); ip++; narg--; break; } *resc = c; *resip = ip; *resnarg = narg; } #endif /* __amd64__ */ static void ktrsigset(const char *name, const l_sigset_t *mask, size_t sz) { unsigned long i, c; printf("%s [ ", name); c = 0; for (i = 1; i <= sz * CHAR_BIT; i++) { if (!LINUX_SIGISMEMBER(*mask, i)) continue; if (c != 0) printf(", "); printf("%s", sysdecode_linux_signal(i)); c++; } if (c == 0) printf("empty ]\n"); else printf(" ]\n"); } bool ktrstruct_linux(const char *name, const char *data, size_t datalen) { l_sigset_t mask; if (strcmp(name, "l_sigset_t") == 0) { /* Old Linux sigset_t is one word size. */ if (datalen < sizeof(int) || datalen > sizeof(l_sigset_t)) return (false); memcpy(&mask, data, datalen); ktrsigset(name, &mask, datalen); } else return (false); return (true); } diff --git a/usr.bin/killall/killall.c b/usr.bin/killall/killall.c index deedd06cada7..8f1b4bad80f2 100644 --- a/usr.bin/killall/killall.c +++ b/usr.bin/killall/killall.c @@ -1,438 +1,437 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2000 Peter Wemm * Copyright (c) 2000 Paul Saab * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void __dead2 usage(void) { fprintf(stderr, "usage: killall [-delmsqvz] [-help] [-I] [-j jail]\n"); fprintf(stderr, " [-u user] [-t tty] [-c cmd] [-SIGNAL] [cmd]...\n"); fprintf(stderr, "At least one option or argument to specify processes must be given.\n"); exit(1); } static void printsig(FILE *fp) { const char *const * p; int cnt; int offset = 0; for (cnt = NSIG, p = sys_signame + 1; --cnt; ++p) { offset += fprintf(fp, "%s ", *p); if (offset >= 75 && cnt > 1) { offset = 0; fprintf(fp, "\n"); } } fprintf(fp, "\n"); } static void nosig(char *name) { warnx("unknown signal %s; valid signals:", name); printsig(stderr); exit(1); } int main(int ac, char **av) { char **saved_av; struct kinfo_proc *procs, *newprocs; struct stat sb; struct passwd *pw; regex_t rgx; regmatch_t pmatch; int i, j, ch; char buf[256]; char first; char *user = NULL; char *tty = NULL; char *cmd = NULL; int qflag = 0; int vflag = 0; int sflag = 0; int dflag = 0; int eflag = 0; int Iflag = 0; int jflag = 0; int mflag = 0; int zflag = 0; uid_t uid = 0; dev_t tdev = 0; pid_t mypid; char thiscmd[MAXCOMLEN + 1]; pid_t thispid; uid_t thisuid; dev_t thistdev; int sig = SIGTERM; const char *const *p; char *ep; int errors = 0; int jid; int mib[4]; size_t miblen; int st, nprocs; size_t size; int matched; int killed = 0; setlocale(LC_ALL, ""); av++; ac--; while (ac > 0) { if (strcmp(*av, "-l") == 0) { printsig(stdout); exit(0); } if (strcmp(*av, "-help") == 0) usage(); if (**av == '-') { ++*av; switch (**av) { case 'j': ++*av; if (**av == '\0') { ++av; --ac; } jflag++; if (*av == NULL) errx(1, "must specify jail"); jid = jail_getid(*av); if (jid < 0) errx(1, "%s", jail_errmsg); if (jail_attach(jid) == -1) err(1, "jail_attach(%d)", jid); break; case 'u': ++*av; if (**av == '\0') { ++av; --ac; } if (*av == NULL) errx(1, "must specify user"); user = *av; break; case 't': ++*av; if (**av == '\0') { ++av; --ac; } if (*av == NULL) errx(1, "must specify tty"); tty = *av; break; case 'c': ++*av; if (**av == '\0') { ++av; --ac; } if (*av == NULL) errx(1, "must specify procname"); cmd = *av; break; case 'q': qflag++; break; case 'v': vflag++; break; case 's': sflag++; break; case 'd': dflag++; break; case 'e': eflag++; break; case 'm': mflag++; break; case 'z': zflag++; break; default: saved_av = av; if (isalpha((unsigned char)**av)) { if (strncasecmp(*av, "SIG", 3) == 0) *av += 3; for (sig = NSIG, p = sys_signame + 1; --sig; ++p) if (strcasecmp(*p, *av) == 0) { sig = p - sys_signame; break; } if (!sig) { if (**saved_av == 'I') { av = saved_av; Iflag = 1; break; } else nosig(*av); } } else if (isdigit((unsigned char)**av)) { sig = strtol(*av, &ep, 10); if (!*av || *ep) errx(1, "illegal signal number: %s", *av); if (sig < 0 || sig >= NSIG) nosig(*av); } else nosig(*av); } ++av; --ac; } else { break; } } if (user == NULL && tty == NULL && cmd == NULL && !jflag && ac == 0) usage(); if (tty) { if (strncmp(tty, "/dev/", 5) == 0) snprintf(buf, sizeof(buf), "%s", tty); else if (strncmp(tty, "tty", 3) == 0 || strncmp(tty, "pts/", 4) == 0) snprintf(buf, sizeof(buf), "/dev/%s", tty); else snprintf(buf, sizeof(buf), "/dev/tty%s", tty); if (stat(buf, &sb) < 0) err(1, "stat(%s)", buf); if (!S_ISCHR(sb.st_mode)) errx(1, "%s: not a character device", buf); tdev = sb.st_rdev; if (dflag) printf("ttydev:0x%jx\n", (uintmax_t)tdev); } if (user) { uid = strtol(user, &ep, 10); if (*user == '\0' || *ep != '\0') { /* was it a number? */ pw = getpwnam(user); if (pw == NULL) errx(1, "user %s does not exist", user); uid = pw->pw_uid; if (dflag) printf("uid:%d\n", uid); } } else { uid = getuid(); if (uid != 0) { pw = getpwuid(uid); if (pw) user = pw->pw_name; if (dflag) printf("uid:%d\n", uid); } } size = 0; mib[0] = CTL_KERN; mib[1] = KERN_PROC; if (user) { mib[2] = eflag ? KERN_PROC_UID : KERN_PROC_RUID; mib[3] = uid; miblen = 4; } else if (tty) { mib[2] = KERN_PROC_TTY; mib[3] = tdev; miblen = 4; } else { mib[2] = KERN_PROC_PROC; mib[3] = 0; miblen = 3; } procs = NULL; st = sysctl(mib, miblen, NULL, &size, NULL, 0); do { size += size / 10; newprocs = realloc(procs, size); if (newprocs == NULL) { free(procs); err(1, "could not reallocate memory"); } procs = newprocs; st = sysctl(mib, miblen, procs, &size, NULL, 0); } while (st == -1 && errno == ENOMEM); if (st == -1) err(1, "could not sysctl(KERN_PROC)"); if (size % sizeof(struct kinfo_proc) != 0) { fprintf(stderr, "proc size mismatch (%zu total, %zu chunks)\n", size, sizeof(struct kinfo_proc)); fprintf(stderr, "userland out of sync with kernel\n"); exit(1); } nprocs = size / sizeof(struct kinfo_proc); if (dflag) printf("nprocs %d\n", nprocs); mypid = getpid(); for (i = 0; i < nprocs; i++) { if (procs[i].ki_stat == SZOMB && !zflag) continue; thispid = procs[i].ki_pid; strlcpy(thiscmd, procs[i].ki_comm, sizeof(thiscmd)); thistdev = procs[i].ki_tdev; if (eflag) thisuid = procs[i].ki_uid; /* effective uid */ else thisuid = procs[i].ki_ruid; /* real uid */ if (thispid == mypid) continue; matched = 1; if (user) { if (thisuid != uid) matched = 0; } if (tty) { if (thistdev != tdev) matched = 0; } if (cmd) { if (mflag) { if (regcomp(&rgx, cmd, REG_EXTENDED|REG_NOSUB) != 0) { mflag = 0; warnx("%s: illegal regexp", cmd); } } if (mflag) { pmatch.rm_so = 0; pmatch.rm_eo = strlen(thiscmd); if (regexec(&rgx, thiscmd, 0, &pmatch, REG_STARTEND) != 0) matched = 0; regfree(&rgx); } else { if (strncmp(thiscmd, cmd, MAXCOMLEN) != 0) matched = 0; } } if (jflag && thispid == getpid()) matched = 0; if (matched == 0) continue; if (ac > 0) matched = 0; for (j = 0; j < ac; j++) { if (mflag) { if (regcomp(&rgx, av[j], REG_EXTENDED|REG_NOSUB) != 0) { mflag = 0; warnx("%s: illegal regexp", av[j]); } } if (mflag) { pmatch.rm_so = 0; pmatch.rm_eo = strlen(thiscmd); if (regexec(&rgx, thiscmd, 0, &pmatch, REG_STARTEND) == 0) matched = 1; regfree(&rgx); } else { if (strcmp(thiscmd, av[j]) == 0) matched = 1; } if (matched) break; } if (matched != 0 && Iflag) { printf("Send signal %d to %s (pid %d uid %d)? ", sig, thiscmd, thispid, thisuid); fflush(stdout); first = ch = getchar(); while (ch != '\n' && ch != EOF) ch = getchar(); if (first != 'y' && first != 'Y') matched = 0; } if (matched == 0) continue; if (dflag) printf("sig:%d, cmd:%s, pid:%d, dev:0x%jx uid:%d\n", sig, thiscmd, thispid, (uintmax_t)thistdev, thisuid); if (vflag || sflag) printf("kill -%s %d\n", sys_signame[sig], thispid); killed++; if (!dflag && !sflag) { if (kill(thispid, sig) < 0 /* && errno != ESRCH */ ) { warn("warning: kill -%s %d", sys_signame[sig], thispid); errors = 1; } } } if (killed == 0) { if (!qflag) fprintf(stderr, "No matching processes %swere found\n", getuid() != 0 ? "belonging to you " : ""); errors = 1; } exit(errors); } diff --git a/usr.bin/ktrace/subr.c b/usr.bin/ktrace/subr.c index 31c09d12e4a2..520ee44a1575 100644 --- a/usr.bin/ktrace/subr.c +++ b/usr.bin/ktrace/subr.c @@ -1,126 +1,122 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #include #include #include "ktrace.h" void timevaladd(struct timeval *, struct timeval *); void timevalsub(struct timeval *, struct timeval *); void timevalfix(struct timeval *); int getpoints(char *s) { int facs = 0; while (*s) { switch(*s) { case 'c': facs |= KTRFAC_SYSCALL | KTRFAC_SYSRET; break; case 'i': facs |= KTRFAC_GENIO; break; case 'f': facs |= KTRFAC_FAULT | KTRFAC_FAULTEND; break; case 'n': facs |= KTRFAC_NAMEI; break; case 'p': facs |= KTRFAC_CAPFAIL; break; case 's': facs |= KTRFAC_PSIG; break; case 't': facs |= KTRFAC_STRUCT; break; case 'u': facs |= KTRFAC_USER; break; case 'w': facs |= KTRFAC_CSW; break; case 'y': facs |= KTRFAC_SYSCTL; break; case '+': facs |= DEF_POINTS; break; default: return (-1); } s++; } return (facs); } void timevaladd(struct timeval *t1, struct timeval *t2) { t1->tv_sec += t2->tv_sec; t1->tv_usec += t2->tv_usec; timevalfix(t1); } void timevalsub(struct timeval *t1, struct timeval *t2) { t1->tv_sec -= t2->tv_sec; t1->tv_usec -= t2->tv_usec; timevalfix(t1); } void timevalfix(struct timeval *t1) { if (t1->tv_usec < 0) { t1->tv_sec--; t1->tv_usec += 1000000; } if (t1->tv_usec >= 1000000) { t1->tv_sec++; t1->tv_usec -= 1000000; } } diff --git a/usr.bin/ktrdump/ktrdump.c b/usr.bin/ktrdump/ktrdump.c index f1c49cbbd418..417c565022da 100644 --- a/usr.bin/ktrdump/ktrdump.c +++ b/usr.bin/ktrdump/ktrdump.c @@ -1,404 +1,403 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2002 Jake Burkholder * Copyright (c) 2004 Robert Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SBUFLEN 128 #define USAGE \ "usage: ktrdump [-cflqrtH] [-i ktrfile] [-M core] [-N system] [-o outfile]\n" static void usage(void) __dead2; static struct nlist nl[] = { { .n_name = "_ktr_version" }, { .n_name = "_ktr_entries" }, { .n_name = "_ktr_idx" }, { .n_name = "_ktr_buf" }, { .n_name = NULL } }; static int cflag; static int fflag; static int lflag; static int Mflag; static int Nflag; static int qflag; static int rflag; static int tflag; static int iflag; static int hflag; static char corefile[PATH_MAX]; static char execfile[PATH_MAX]; static char outfile[PATH_MAX] = "stdout"; static char desc[SBUFLEN]; static char errbuf[_POSIX2_LINE_MAX]; static char fbuf[PATH_MAX]; static char obuf[PATH_MAX]; static char sbuf[KTR_PARMS][SBUFLEN]; /* * Reads the ktr trace buffer from kernel memory and prints the trace entries. */ int main(int ac, char **av) { u_long parms[KTR_PARMS]; struct ktr_entry *buf; uintmax_t tlast, tnow; unsigned long bufptr; cap_rights_t rights; struct stat sb; kvm_t *kd; FILE *out; char *p; int version; int entries; int count; int index, index2; int parm; int in; int c; int i = 0; /* * Parse commandline arguments. */ out = stdout; while ((c = getopt(ac, av, "cflqrtHe:i:m:M:N:o:")) != -1) switch (c) { case 'c': cflag = 1; break; case 'N': case 'e': if (strlcpy(execfile, optarg, sizeof(execfile)) >= sizeof(execfile)) errx(1, "%s: File name too long", optarg); Nflag = 1; break; case 'f': fflag = 1; break; case 'i': iflag = 1; if ((in = open(optarg, O_RDONLY)) == -1) err(1, "%s", optarg); cap_rights_init(&rights, CAP_FSTAT, CAP_MMAP_R); if (caph_rights_limit(in, &rights) < 0) err(1, "unable to limit rights for %s", optarg); break; case 'l': lflag = 1; break; case 'M': case 'm': if (strlcpy(corefile, optarg, sizeof(corefile)) >= sizeof(corefile)) errx(1, "%s: File name too long", optarg); Mflag = 1; break; case 'o': if ((out = fopen(optarg, "w")) == NULL) err(1, "%s", optarg); strlcpy(outfile, optarg, sizeof(outfile)); break; case 'q': qflag++; break; case 'r': rflag = 1; break; case 't': tflag = 1; break; case 'H': hflag = 1; break; case '?': default: usage(); } ac -= optind; av += optind; if (ac != 0) usage(); if (caph_limit_stream(fileno(out), CAPH_WRITE) < 0) err(1, "unable to limit rights for %s", outfile); if (caph_limit_stderr() < 0) err(1, "unable to limit rights for stderr"); /* * Open our execfile and corefile, resolve needed symbols and read in * the trace buffer. */ if ((kd = kvm_openfiles(Nflag ? execfile : NULL, Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL) errx(1, "%s", errbuf); /* * Cache NLS data, for strerror, for err(3), before entering capability * mode. */ caph_cache_catpages(); count = kvm_nlist(kd, nl); if (count == -1) errx(1, "%s", kvm_geterr(kd)); if (count > 0) errx(1, "failed to resolve ktr symbols"); if (kvm_read(kd, nl[0].n_value, &version, sizeof(version)) == -1) errx(1, "%s", kvm_geterr(kd)); if (version != KTR_VERSION) errx(1, "ktr version mismatch"); /* * Enter Capsicum sandbox. * * kvm_nlist() above uses kldsym(2) for native kernels, and that isn't * allowed in the sandbox. */ if (caph_enter() < 0) err(1, "unable to enter capability mode"); if (iflag) { if (fstat(in, &sb) == -1) errx(1, "stat"); entries = sb.st_size / sizeof(*buf); index = 0; buf = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, in, 0); if (buf == MAP_FAILED) errx(1, "mmap"); } else { if (kvm_read(kd, nl[1].n_value, &entries, sizeof(entries)) == -1) errx(1, "%s", kvm_geterr(kd)); if ((buf = malloc(sizeof(*buf) * entries)) == NULL) err(1, NULL); if (kvm_read(kd, nl[2].n_value, &index, sizeof(index)) == -1 || kvm_read(kd, nl[3].n_value, &bufptr, sizeof(bufptr)) == -1 || kvm_read(kd, bufptr, buf, sizeof(*buf) * entries) == -1 || kvm_read(kd, nl[2].n_value, &index2, sizeof(index2)) == -1) errx(1, "%s", kvm_geterr(kd)); } /* * Print a nice header. */ if (!qflag) { fprintf(out, "%-6s ", "index"); if (cflag) fprintf(out, "%-3s ", "cpu"); if (tflag) fprintf(out, "%-16s ", "timestamp"); if (fflag) fprintf(out, "%-40s ", "file and line"); if (hflag) fprintf(out, "%-18s ", "tid"); fprintf(out, "%s", "trace"); fprintf(out, "\n"); fprintf(out, "------ "); if (cflag) fprintf(out, "--- "); if (tflag) fprintf(out, "---------------- "); if (fflag) fprintf(out, "---------------------------------------- "); if (hflag) fprintf(out, "------------------ "); fprintf(out, "----- "); fprintf(out, "\n"); } tlast = UINTPTR_MAX; /* * Now tear through the trace buffer. * * In "live" mode, find the oldest entry (first non-NULL entry * after index2) and walk forward. Otherwise, start with the * most recent entry and walk backwards. */ if (!iflag) { if (lflag) { i = index2 + 1 % entries; while (buf[i].ktr_desc == NULL && i != index) { i++; if (i == entries) i = 0; } } else { i = index - 1; if (i < 0) i = entries - 1; } } dump_entries: for (;;) { if (buf[i].ktr_desc == NULL) break; if (kvm_read(kd, (u_long)buf[i].ktr_desc, desc, sizeof(desc)) == -1) errx(1, "%s", kvm_geterr(kd)); desc[sizeof(desc) - 1] = '\0'; parm = 0; for (p = desc; (c = *p++) != '\0';) { if (c != '%') continue; next: if ((c = *p++) == '\0') break; if (c == '%') continue; if (parm == KTR_PARMS) errx(1, "too many parameters in \"%s\"", desc); switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '#': case '-': case ' ': case '+': case '\'': case 'h': case 'l': case 'j': case 't': case 'z': case 'q': case 'L': case '.': goto next; case 's': if (kvm_read(kd, (u_long)buf[i].ktr_parms[parm], sbuf[parm], sizeof(sbuf[parm])) == -1) strcpy(sbuf[parm], "(null)"); sbuf[parm][sizeof(sbuf[0]) - 1] = '\0'; parms[parm] = (u_long)sbuf[parm]; parm++; break; default: parms[parm] = buf[i].ktr_parms[parm]; parm++; break; } } fprintf(out, "%6d ", i); if (cflag) fprintf(out, "%3d ", buf[i].ktr_cpu); if (tflag) { tnow = (uintmax_t)buf[i].ktr_timestamp; if (rflag) { if (tlast == UINTPTR_MAX) tlast = tnow; fprintf(out, "%16ju ", !iflag ? tlast - tnow : tnow - tlast); tlast = tnow; } else fprintf(out, "%16ju ", tnow); } if (fflag) { if (kvm_read(kd, (u_long)buf[i].ktr_file, fbuf, sizeof(fbuf)) == -1) strcpy(fbuf, "(null)"); snprintf(obuf, sizeof(obuf), "%s:%d", fbuf, buf[i].ktr_line); fprintf(out, "%-40s ", obuf); } if (hflag) fprintf(out, "%p ", buf[i].ktr_thread); fprintf(out, desc, parms[0], parms[1], parms[2], parms[3], parms[4], parms[5]); fprintf(out, "\n"); if (!iflag) { /* * 'index' and 'index2' are the values of 'ktr_idx' * before and after the KTR buffer was copied into * 'buf'. Since the KTR entries between 'index' and * 'index2' were in flux while the KTR buffer was * being copied to userspace we don't dump them. */ if (lflag) { if (++i == entries) i = 0; if (i == index) break; } else { if (i == index2) break; if (--i < 0) i = entries - 1; } } else { if (++i == entries) break; } } /* * In "live" mode, poll 'ktr_idx' periodically and dump any * new entries since our last pass through the ring. */ if (lflag && !iflag) { while (index == index2) { usleep(50 * 1000); if (kvm_read(kd, nl[2].n_value, &index2, sizeof(index2)) == -1) errx(1, "%s", kvm_geterr(kd)); } i = index; index = index2; if (kvm_read(kd, bufptr, buf, sizeof(*buf) * entries) == -1 || kvm_read(kd, nl[2].n_value, &index2, sizeof(index2)) == -1) errx(1, "%s", kvm_geterr(kd)); goto dump_entries; } return (0); } static void usage(void) { fprintf(stderr, USAGE); exit(1); } diff --git a/usr.bin/lastcomm/readrec.c b/usr.bin/lastcomm/readrec.c index b99e76645402..a8665a6f76ac 100644 --- a/usr.bin/lastcomm/readrec.c +++ b/usr.bin/lastcomm/readrec.c @@ -1,263 +1,262 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2007 Diomidis Spinellis * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include #include #include #include #include #include #include #include #include int readrec_forward(FILE *f, struct acctv3 *av2); int readrec_backward(FILE *f, struct acctv3 *av2); /* * Reverse offsetof: return the offset of field f * from the end of the structure s. */ #define roffsetof(s, f) (sizeof(s) - offsetof(s, f)) /* * Read exactly one record of size size from stream f into ptr. * Failure to read the complete record is considered a file format error, * and will set errno to EFTYPE. * Return 0 on success, EOF on end of file or error. */ static int fread_record(void *ptr, size_t size, FILE *f) { size_t rv; if ((rv = fread(ptr, 1, size, f)) == size) return (0); else if (ferror(f) || rv == 0) return (EOF); else { /* Short read. */ errno = EFTYPE; return (EOF); } } /* * Return the value of a comp_t field. */ static float decode_comp(comp_t v) { int result, exp; result = v & 017777; for (exp = v >> 13; exp; exp--) result <<= 3; return ((double)result / AHZV1); } /* * Read a v1 accounting record stored at the current * position of stream f. * Convert the data to the current record format. * Return EOF on error or end-of-file. */ static int readrec_v1(FILE *f, struct acctv3 *av3) { struct acctv1 av1; int rv; if ((rv = fread_record(&av1, sizeof(av1), f)) == EOF) return (EOF); av3->ac_zero = 0; av3->ac_version = 3; av3->ac_len = av3->ac_len2 = sizeof(*av3); memcpy(av3->ac_comm, av1.ac_comm, AC_COMM_LEN); av3->ac_utime = decode_comp(av1.ac_utime) * 1000000; av3->ac_stime = decode_comp(av1.ac_stime) * 1000000; av3->ac_etime = decode_comp(av1.ac_etime) * 1000000; av3->ac_btime = av1.ac_btime; av3->ac_uid = av1.ac_uid; av3->ac_gid = av1.ac_gid; av3->ac_mem = av1.ac_mem; av3->ac_io = decode_comp(av1.ac_io); av3->ac_tty = av1.ac_tty; av3->ac_flagx = av1.ac_flag | ANVER; return (0); } /* * Read an v2 accounting record stored at the current * position of stream f. * Return EOF on error or end-of-file. */ static int readrec_v2(FILE *f, struct acctv3 *av3) { struct acctv2 av2; int rv; if ((rv = fread_record(&av2, sizeof(av2), f)) == EOF) return (EOF); av3->ac_zero = 0; av3->ac_version = 3; av3->ac_len = av3->ac_len2 = sizeof(*av3); memcpy(av3->ac_comm, av2.ac_comm, AC_COMM_LEN); av3->ac_utime = av2.ac_utime; av3->ac_stime = av2.ac_stime; av3->ac_etime = av2.ac_etime; av3->ac_btime = av2.ac_btime; av3->ac_uid = av2.ac_uid; av3->ac_gid = av2.ac_gid; av3->ac_mem = av2.ac_mem; av3->ac_io = av2.ac_io; av3->ac_tty = av2.ac_tty; av3->ac_flagx = av2.ac_flagx; return (0); } /* * Read an v2 accounting record stored at the current * position of stream f. * Return EOF on error or end-of-file. */ static int readrec_v3(FILE *f, struct acctv3 *av3) { return (fread_record(av3, sizeof(*av3), f)); } /* * Read a new-style (post-v1) accounting record stored at * the current position of stream f. * Convert the data to the current record format. * Return EOF on error or end-of-file. */ static int readrec_vx(FILE *f, struct acctv3 *av3) { uint8_t magic, version; if (fread_record(&magic, sizeof(magic), f) == EOF || fread_record(&version, sizeof(version), f) == EOF || ungetc(version, f) == EOF || ungetc(magic, f) == EOF) return (EOF); switch (version) { case 2: return (readrec_v2(f, av3)); case 3: return (readrec_v3(f, av3)); /* Add handling for more versions here. */ default: errno = EFTYPE; return (EOF); } } /* * Read an accounting record stored at the current * position of stream f. * Old-format records are converted to the current record * format. * Return the number of records read (1 or 0 at the end-of-file), * or EOF on error. */ int readrec_forward(FILE *f, struct acctv3 *av3) { int magic, rv; if ((magic = getc(f)) == EOF) return (ferror(f) ? EOF : 0); if (ungetc(magic, f) == EOF) return (EOF); if (magic != 0) /* Old record format. */ rv = readrec_v1(f, av3); else /* New record formats. */ rv = readrec_vx(f, av3); return (rv == EOF ? EOF : 1); } /* * Read an accounting record ending at the current * position of stream f. * Old-format records are converted to the current record * format. * The file pointer is positioned at the beginning of the * record read. * Return the number of records read (1 or 0 at the end-of-file), * or EOF on error. */ int readrec_backward(FILE *f, struct acctv3 *av3) { off_t pos; int c; uint16_t len; if ((pos = ftell(f)) == -1) return (EOF); if (pos == 0) return (0); if (fseek(f, -roffsetof(struct acctv3, ac_trailer), SEEK_CUR) == EOF || (c = getc(f)) == EOF) return (EOF); if (c & ANVER) { /* * New record formats. For v2 and v3 offset from the * end for ac_len2 should be same. */ if (fseeko(f, pos - roffsetof(struct acctv2, ac_len2), SEEK_SET) == EOF || fread_record(&len, sizeof(len), f) == EOF || fseeko(f, pos - len, SEEK_SET) == EOF || readrec_vx(f, av3) == EOF || fseeko(f, pos - len, SEEK_SET) == EOF) return (EOF); else return (1); } else { /* Old record format. */ if (fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF || readrec_v1(f, av3) == EOF || fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF) return (EOF); else return (1); } } diff --git a/usr.bin/ldd/ldd.c b/usr.bin/ldd/ldd.c index 5dcac0e56c2f..1c0fc0fbc2ea 100644 --- a/usr.bin/ldd/ldd.c +++ b/usr.bin/ldd/ldd.c @@ -1,472 +1,471 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1993 Paul Kranenburg * 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 Paul Kranenburg. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 32-bit ELF data structures can only be used if the system header[s] declare * them. There is no official macro for determining whether they are declared, * so check for the existence of one of the 32-macros defined in elf(5). */ #ifdef ELF32_R_TYPE #define ELF32_SUPPORTED #endif #define LDD_SETENV(name, value, overwrite) do { \ setenv("LD_" name, value, overwrite); \ setenv("LD_32_" name, value, overwrite); \ } while (0) #define LDD_UNSETENV(name) do { \ unsetenv("LD_" name); \ unsetenv("LD_32_" name); \ } while (0) static int is_executable(const char *fname, int fd, int *is_shlib, int *type); static void usage(void); #define TYPE_UNKNOWN 0 #define TYPE_ELF 1 /* Architecture default */ #if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED) #define TYPE_ELF32 2 /* Explicit 32 bits on architectures >32 bits */ #define _PATH_LDD32 "/usr/bin/ldd32" static int execldd32(char *file, char *fmt1, char *fmt2, int aflag) { char *argv[9]; int i, rval, status; LDD_UNSETENV("TRACE_LOADED_OBJECTS"); rval = 0; i = 0; argv[i++] = strdup(_PATH_LDD32); if (aflag) argv[i++] = strdup("-a"); if (fmt1 != NULL) { argv[i++] = strdup("-f"); argv[i++] = strdup(fmt1); } if (fmt2 != NULL) { argv[i++] = strdup("-f"); argv[i++] = strdup(fmt2); } argv[i++] = strdup(file); argv[i++] = NULL; switch (fork()) { case -1: err(1, "fork"); break; case 0: execv(_PATH_LDD32, argv); warn("%s", _PATH_LDD32); _exit(127); break; default: if (wait(&status) < 0) rval = 1; else if (WIFSIGNALED(status)) rval = 1; else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) rval = 1; break; } while (i--) free(argv[i]); LDD_SETENV("TRACE_LOADED_OBJECTS", "yes", 1); return (rval); } #endif int main(int argc, char *argv[]) { char *fmt1, *fmt2; const char *rtld; int aflag, c, fd, rval, status, is_shlib, rv, type; aflag = 0; fmt1 = fmt2 = NULL; while ((c = getopt(argc, argv, "af:")) != -1) { switch (c) { case 'a': aflag++; break; case 'f': if (fmt1 != NULL) { if (fmt2 != NULL) errx(1, "too many formats"); fmt2 = optarg; } else fmt1 = optarg; break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc <= 0) { usage(); /* NOTREACHED */ } rval = 0; for (; argc > 0; argc--, argv++) { if ((fd = open(*argv, O_RDONLY | O_VERIFY, 0)) < 0) { warn("%s", *argv); rval |= 1; continue; } rv = is_executable(*argv, fd, &is_shlib, &type); close(fd); if (rv == 0) { rval |= 1; continue; } switch (type) { case TYPE_ELF: break; #if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED) case TYPE_ELF32: rval |= execldd32(*argv, fmt1, fmt2, aflag); continue; #endif case TYPE_UNKNOWN: default: /* * This shouldn't happen unless is_executable() * is broken. */ errx(EDOOFUS, "unknown executable type"); } /* ld.so magic */ LDD_SETENV("TRACE_LOADED_OBJECTS", "yes", 1); if (fmt1 != NULL) LDD_SETENV("TRACE_LOADED_OBJECTS_FMT1", fmt1, 1); if (fmt2 != NULL) LDD_SETENV("TRACE_LOADED_OBJECTS_FMT2", fmt2, 1); LDD_SETENV("TRACE_LOADED_OBJECTS_PROGNAME", *argv, 1); if (aflag) LDD_SETENV("TRACE_LOADED_OBJECTS_ALL", "1", 1); else if (fmt1 == NULL && fmt2 == NULL) /* Default formats */ printf("%s:\n", *argv); fflush(stdout); switch (fork()) { case -1: err(1, "fork"); break; default: if (wait(&status) < 0) { warn("wait"); rval |= 1; } else if (WIFSIGNALED(status)) { fprintf(stderr, "%s: signal %d\n", *argv, WTERMSIG(status)); rval |= 1; } else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { fprintf(stderr, "%s: exit status %d\n", *argv, WEXITSTATUS(status)); rval |= 1; } break; case 0: rtld = _PATH_RTLD; #if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED) if (type == TYPE_ELF32) rtld = __PATH_RTLD("32"); #endif if (is_shlib == 0) { execl(rtld, rtld, "--", *argv, (char *)NULL); warn("%s", *argv); } else if (fmt1 == NULL && fmt2 == NULL && !aflag) { dlopen(*argv, RTLD_TRACE); warnx("%s: %s", *argv, dlerror()); } else { execl(rtld, rtld, "-d", "--", *argv, (char *)NULL); } _exit(1); } } return (rval); } static void usage(void) { fprintf(stderr, "usage: ldd [-a] [-f format [-f format]] program ...\n"); exit(1); } static bool has_freebsd_abi_tag(const char *fname, Elf *elf, GElf_Ehdr *ehdr, off_t offset, size_t len) { Elf_Data dst, src; const Elf_Note *note; char *buf; const char *name; void *copy; size_t namesz, descsz; bool has_abi_tag; buf = elf_rawfile(elf, NULL); if (buf == NULL) { warnx("%s: %s", fname, elf_errmsg(0)); return (false); } memset(&src, 0, sizeof(src)); src.d_buf = buf + offset; src.d_size = len; src.d_type = ELF_T_NOTE; src.d_version = EV_CURRENT; memset(&dst, 0, sizeof(dst)); dst.d_buf = copy = malloc(len); dst.d_size = len; dst.d_type = ELF_T_NOTE; dst.d_version = EV_CURRENT; if (gelf_xlatetom(elf, &dst, &src, ehdr->e_ident[EI_DATA]) == NULL) { warnx("%s: failed to parse notes: %s", fname, elf_errmsg(0)); free(copy); return (false); } buf = copy; has_abi_tag = false; for (;;) { if (len < sizeof(*note)) break; note = (const void *)buf; buf += sizeof(*note); len -= sizeof(*note); namesz = roundup2(note->n_namesz, sizeof(uint32_t)); descsz = roundup2(note->n_descsz, sizeof(uint32_t)); if (len < namesz + descsz) break; name = buf; if (note->n_namesz == sizeof(ELF_NOTE_FREEBSD) && strncmp(name, ELF_NOTE_FREEBSD, note->n_namesz) == 0 && note->n_type == NT_FREEBSD_ABI_TAG && note->n_descsz == sizeof(uint32_t)) { has_abi_tag = true; break; } buf += namesz + descsz; len -= namesz + descsz; } free(copy); return (has_abi_tag); } static bool is_pie(const char *fname, Elf *elf, GElf_Ehdr *ehdr, off_t offset, size_t len) { Elf_Data dst, src; char *buf; void *copy; const GElf_Dyn *dyn; size_t dynsize; u_int count, i; bool pie; buf = elf_rawfile(elf, NULL); if (buf == NULL) { warnx("%s: %s", fname, elf_errmsg(0)); return (false); } dynsize = gelf_fsize(elf, ELF_T_DYN, 1, EV_CURRENT); if (dynsize == 0) { warnx("%s: %s", fname, elf_errmsg(0)); return (false); } count = len / dynsize; memset(&src, 0, sizeof(src)); src.d_buf = buf + offset; src.d_size = len; src.d_type = ELF_T_DYN; src.d_version = EV_CURRENT; memset(&dst, 0, sizeof(dst)); dst.d_buf = copy = malloc(count * sizeof(*dyn)); dst.d_size = count * sizeof(*dyn); dst.d_type = ELF_T_DYN; dst.d_version = EV_CURRENT; if (gelf_xlatetom(elf, &dst, &src, ehdr->e_ident[EI_DATA]) == NULL) { warnx("%s: failed to parse .dynamic: %s", fname, elf_errmsg(0)); free(copy); return (false); } dyn = copy; pie = false; for (i = 0; i < count; i++) { if (dyn[i].d_tag != DT_FLAGS_1) continue; pie = (dyn[i].d_un.d_val & DF_1_PIE) != 0; break; } free(copy); return (pie); } static int is_executable(const char *fname, int fd, int *is_shlib, int *type) { Elf *elf; GElf_Ehdr ehdr; GElf_Phdr phdr; bool dynamic, freebsd, pie; int i; *is_shlib = 0; *type = TYPE_UNKNOWN; dynamic = false; freebsd = false; pie = false; if (elf_version(EV_CURRENT) == EV_NONE) { warnx("unsupported libelf"); return (0); } elf = elf_begin(fd, ELF_C_READ, NULL); if (elf == NULL) { warnx("%s: %s", fname, elf_errmsg(0)); return (0); } if (elf_kind(elf) != ELF_K_ELF) { elf_end(elf); warnx("%s: not a dynamic ELF executable", fname); return (0); } if (gelf_getehdr(elf, &ehdr) == NULL) { warnx("%s: %s", fname, elf_errmsg(0)); elf_end(elf); return (0); } *type = TYPE_ELF; #if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED) if (gelf_getclass(elf) == ELFCLASS32) { *type = TYPE_ELF32; } #endif freebsd = ehdr.e_ident[EI_OSABI] == ELFOSABI_FREEBSD; for (i = 0; i < ehdr.e_phnum; i++) { if (gelf_getphdr(elf, i, &phdr) == NULL) { warnx("%s: %s", fname, elf_errmsg(0)); elf_end(elf); return (0); } switch (phdr.p_type) { case PT_NOTE: if (ehdr.e_ident[EI_OSABI] == ELFOSABI_NONE && !freebsd) freebsd = has_freebsd_abi_tag(fname, elf, &ehdr, phdr.p_offset, phdr.p_filesz); break; case PT_DYNAMIC: dynamic = true; if (ehdr.e_type == ET_DYN) pie = is_pie(fname, elf, &ehdr, phdr.p_offset, phdr.p_filesz); break; } } if (!dynamic) { elf_end(elf); warnx("%s: not a dynamic ELF executable", fname); return (0); } if (ehdr.e_type == ET_DYN && !pie) { *is_shlib = 1; if (!freebsd) { elf_end(elf); warnx("%s: not a FreeBSD ELF shared object", fname); return (0); } } elf_end(elf); return (1); } diff --git a/usr.bin/localedef/charmap.c b/usr.bin/localedef/charmap.c index 1da4d8921ecb..a8d723163e74 100644 --- a/usr.bin/localedef/charmap.c +++ b/usr.bin/localedef/charmap.c @@ -1,395 +1,395 @@ /*- * Copyright 2010 Nexenta Systems, Inc. All rights reserved. * Copyright 2015 John Marino * * This source code is derived from the illumos localedef command, and * provided under BSD-style license terms by Nexenta Systems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. */ /* * CHARMAP file handling for localedef. */ -#include + #include #include #include #include #include #include #include #include #include "localedef.h" #include "parser.h" typedef struct charmap { const char *name; wchar_t wc; RB_ENTRY(charmap) rb_sym; RB_ENTRY(charmap) rb_wc; } charmap_t; static int cmap_compare_sym(const void *n1, const void *n2); static int cmap_compare_wc(const void *n1, const void *n2); static RB_HEAD(cmap_sym, charmap) cmap_sym; static RB_HEAD(cmap_wc, charmap) cmap_wc; RB_GENERATE_STATIC(cmap_sym, charmap, rb_sym, cmap_compare_sym); RB_GENERATE_STATIC(cmap_wc, charmap, rb_wc, cmap_compare_wc); /* * Array of POSIX specific portable characters. */ static const struct { const char *name; int ch; } portable_chars[] = { { "NUL", '\0' }, { "SOH", '\x01' }, { "STX", '\x02' }, { "ETX", '\x03' }, { "EOT", '\x04' }, { "ENQ", '\x05' }, { "ACK", '\x06' }, { "BEL", '\a' }, { "alert", '\a' }, { "BS", '\b' }, { "backspace", '\b' }, { "HT", '\t' }, { "tab", '\t' }, { "LF", '\n' }, { "newline", '\n' }, { "VT", '\v' }, { "vertical-tab", '\v' }, { "FF", '\f' }, { "form-feed", '\f' }, { "CR", '\r' }, { "carriage-return", '\r' }, { "SO", '\x0e' }, { "SI", '\x0f' }, { "DLE", '\x10' }, { "DC1", '\x11' }, { "DC2", '\x12' }, { "DC3", '\x13' }, { "DC4", '\x14' }, { "NAK", '\x15' }, { "SYN", '\x16' }, { "ETB", '\x17' }, { "CAN", '\x18' }, { "EM", '\x19' }, { "SUB", '\x1a' }, { "ESC", '\x1b' }, { "FS", '\x1c' }, { "IS4", '\x1c' }, { "GS", '\x1d' }, { "IS3", '\x1d' }, { "RS", '\x1e' }, { "IS2", '\x1e' }, { "US", '\x1f' }, { "IS1", '\x1f' }, { "DEL", '\x7f' }, { "space", ' ' }, { "exclamation-mark", '!' }, { "quotation-mark", '"' }, { "number-sign", '#' }, { "dollar-sign", '$' }, { "percent-sign", '%' }, { "ampersand", '&' }, { "apostrophe", '\'' }, { "left-parenthesis", '(' }, { "right-parenthesis", ')' }, { "asterisk", '*' }, { "plus-sign", '+' }, { "comma", ','}, { "hyphen-minus", '-' }, { "hyphen", '-' }, { "full-stop", '.' }, { "period", '.' }, { "slash", '/' }, { "solidus", '/' }, { "zero", '0' }, { "one", '1' }, { "two", '2' }, { "three", '3' }, { "four", '4' }, { "five", '5' }, { "six", '6' }, { "seven", '7' }, { "eight", '8' }, { "nine", '9' }, { "colon", ':' }, { "semicolon", ';' }, { "less-than-sign", '<' }, { "equals-sign", '=' }, { "greater-than-sign", '>' }, { "question-mark", '?' }, { "commercial-at", '@' }, { "left-square-bracket", '[' }, { "backslash", '\\' }, { "reverse-solidus", '\\' }, { "right-square-bracket", ']' }, { "circumflex", '^' }, { "circumflex-accent", '^' }, { "low-line", '_' }, { "underscore", '_' }, { "grave-accent", '`' }, { "left-brace", '{' }, { "left-curly-bracket", '{' }, { "vertical-line", '|' }, { "right-brace", '}' }, { "right-curly-bracket", '}' }, { "tilde", '~' }, { "A", 'A' }, { "B", 'B' }, { "C", 'C' }, { "D", 'D' }, { "E", 'E' }, { "F", 'F' }, { "G", 'G' }, { "H", 'H' }, { "I", 'I' }, { "J", 'J' }, { "K", 'K' }, { "L", 'L' }, { "M", 'M' }, { "N", 'N' }, { "O", 'O' }, { "P", 'P' }, { "Q", 'Q' }, { "R", 'R' }, { "S", 'S' }, { "T", 'T' }, { "U", 'U' }, { "V", 'V' }, { "W", 'W' }, { "X", 'X' }, { "Y", 'Y' }, { "Z", 'Z' }, { "a", 'a' }, { "b", 'b' }, { "c", 'c' }, { "d", 'd' }, { "e", 'e' }, { "f", 'f' }, { "g", 'g' }, { "h", 'h' }, { "i", 'i' }, { "j", 'j' }, { "k", 'k' }, { "l", 'l' }, { "m", 'm' }, { "n", 'n' }, { "o", 'o' }, { "p", 'p' }, { "q", 'q' }, { "r", 'r' }, { "s", 's' }, { "t", 't' }, { "u", 'u' }, { "v", 'v' }, { "w", 'w' }, { "x", 'x' }, { "y", 'y' }, { "z", 'z' }, { NULL, 0 } }; static int cmap_compare_sym(const void *n1, const void *n2) { const charmap_t *c1 = n1; const charmap_t *c2 = n2; int rv; rv = strcmp(c1->name, c2->name); return ((rv < 0) ? -1 : (rv > 0) ? 1 : 0); } static int cmap_compare_wc(const void *n1, const void *n2) { const charmap_t *c1 = n1; const charmap_t *c2 = n2; return ((c1->wc < c2->wc) ? -1 : (c1->wc > c2->wc) ? 1 : 0); } void init_charmap(void) { RB_INIT(&cmap_sym); RB_INIT(&cmap_wc); } static void add_charmap_impl(const char *sym, wchar_t wc, int nodups) { charmap_t srch; charmap_t *n = NULL; srch.wc = wc; srch.name = sym; /* * also possibly insert the wide mapping, although note that there * can only be one of these per wide character code. */ if ((wc != (wchar_t)-1) && ((RB_FIND(cmap_wc, &cmap_wc, &srch)) == NULL)) { if ((n = calloc(1, sizeof (*n))) == NULL) { errf("out of memory"); return; } n->wc = wc; RB_INSERT(cmap_wc, &cmap_wc, n); } if (sym) { if (RB_FIND(cmap_sym, &cmap_sym, &srch) != NULL) { if (nodups) { errf("duplicate character definition"); } return; } if ((n == NULL) && ((n = calloc(1, sizeof (*n))) == NULL)) { errf("out of memory"); return; } n->wc = wc; n->name = sym; RB_INSERT(cmap_sym, &cmap_sym, n); } } void add_charmap(const char *sym, int c) { add_charmap_impl(sym, c, 1); } void add_charmap_undefined(char *sym) { charmap_t srch; charmap_t *cm = NULL; srch.name = sym; cm = RB_FIND(cmap_sym, &cmap_sym, &srch); if ((undefok == 0) && ((cm == NULL) || (cm->wc == (wchar_t)-1))) { warn("undefined symbol <%s>", sym); add_charmap_impl(sym, -1, 0); } else { free(sym); } } void add_charmap_range(char *s, char *e, int wc) { int ls, le; int si; int sn, en; int i; static const char *digits = "0123456789"; ls = strlen(s); le = strlen(e); if (((si = strcspn(s, digits)) == 0) || (si == ls) || (strncmp(s, e, si) != 0) || ((int)strspn(s + si, digits) != (ls - si)) || ((int)strspn(e + si, digits) != (le - si)) || ((sn = atoi(s + si)) > ((en = atoi(e + si))))) { errf("malformed charmap range"); return; } s[si] = 0; for (i = sn; i <= en; i++) { char *nn; (void) asprintf(&nn, "%s%0*u", s, ls - si, i); if (nn == NULL) { errf("out of memory"); return; } add_charmap_impl(nn, wc, 1); wc++; } free(s); free(e); } void add_charmap_char(const char *name, int val) { add_charmap_impl(name, val, 0); } /* * POSIX insists that certain entries be present, even when not in the * original charmap file. */ void add_charmap_posix(void) { int i; for (i = 0; portable_chars[i].name; i++) { add_charmap_char(portable_chars[i].name, portable_chars[i].ch); } } int lookup_charmap(const char *sym, wchar_t *wc) { charmap_t srch; charmap_t *n; srch.name = sym; n = RB_FIND(cmap_sym, &cmap_sym, &srch); if (n && n->wc != (wchar_t)-1) { if (wc) *wc = n->wc; return (0); } return (-1); } int check_charmap(wchar_t wc) { charmap_t srch; srch.wc = wc; return (RB_FIND(cmap_wc, &cmap_wc, &srch) ? 0 : -1); } diff --git a/usr.bin/localedef/collate.c b/usr.bin/localedef/collate.c index 2a080773a95e..830235036044 100644 --- a/usr.bin/localedef/collate.c +++ b/usr.bin/localedef/collate.c @@ -1,1329 +1,1329 @@ /*- * Copyright 2018 Nexenta Systems, Inc. * Copyright 2015 John Marino * * This source code is derived from the illumos localedef command, and * provided under BSD-style license terms by Nexenta Systems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. */ /* * LC_COLLATE database generation routines for localedef. */ -#include + #include #include #include #include #include #include #include #include #include #include #include "localedef.h" #include "parser.h" #include "collate.h" _Static_assert(COLL_WEIGHTS_MAX == 10, "This code assumes a value of 10"); /* * Design notes. * * It will be extremely helpful to the reader if they have access to * the localedef and locale file format specifications available. * Latest versions of these are available from www.opengroup.org. * * The design for the collation code is a bit complex. The goal is a * single collation database as described in collate.h (in * libc/port/locale). However, there are some other tidbits: * * a) The substitution entries are now a directly indexable array. A * priority elsewhere in the table is taken as an index into the * substitution table if it has a high bit (COLLATE_SUBST_PRIORITY) * set. (The bit is cleared and the result is the index into the * table. * * b) We eliminate duplicate entries into the substitution table. * This saves a lot of space. * * c) The priorities for each level are "compressed", so that each * sorting level has consecutively numbered priorities starting at 1. * (O is reserved for the ignore priority.) This means sort levels * which only have a few distinct priorities can represent the * priority level in fewer bits, which makes the strxfrm output * smaller. * * d) We record the total number of priorities so that strxfrm can * figure out how many bytes to expand a numeric priority into. * * e) For the UNDEFINED pass (the last pass), we record the maximum * number of bits needed to uniquely prioritize these entries, so that * the last pass can also use smaller strxfrm output when possible. * * f) Priorities with the sign bit set are verboten. This works out * because no active character set needs that bit to carry significant * information once the character is in wide form. * * To process the entire data to make the database, we actually run * multiple passes over the data. * * The first pass, which is done at parse time, identifies elements, * substitutions, and such, and records them in priority order. As * some priorities can refer to other priorities, using forward * references, we use a table of references indicating whether the * priority's value has been resolved, or whether it is still a * reference. * * The second pass walks over all the items in priority order, noting * that they are used directly, and not just an indirect reference. * This is done by creating a "weight" structure for the item. The * weights are stashed in an RB tree sorted by relative "priority". * * The third pass walks over all the weight structures, in priority * order, and assigns a new monotonically increasing (per sort level) * weight value to them. These are the values that will actually be * written to the file. * * The fourth pass just writes the data out. */ /* * In order to resolve the priorities, we create a table of priorities. * Entries in the table can be in one of three states. * * UNKNOWN is for newly allocated entries, and indicates that nothing * is known about the priority. (For example, when new entries are created * for collating-symbols, this is the value assigned for them until the * collating symbol's order has been determined. * * RESOLVED is used for an entry where the priority indicates the final * numeric weight. * * REFER is used for entries that reference other entries. Typically * this is used for forward references. A collating-symbol can never * have this value. * * The "pass" field is used during final resolution to aid in detection * of referencing loops. (For example depends on , but has its * priority dependent on .) */ typedef enum { UNKNOWN, /* priority is totally unknown */ RESOLVED, /* priority value fully resolved */ REFER /* priority is a reference (index) */ } res_t; typedef struct weight { int32_t pri; int opt; RB_ENTRY(weight) entry; } weight_t; typedef struct priority { res_t res; int32_t pri; int pass; int lineno; } collpri_t; #define NUM_WT collinfo.directive_count /* * These are the abstract collating symbols, which are just a symbolic * way to reference a priority. */ struct collsym { char *name; int32_t ref; RB_ENTRY(collsym) entry; }; /* * These are also abstract collating symbols, but we allow them to have * different priorities at different levels. */ typedef struct collundef { char *name; int32_t ref[COLL_WEIGHTS_MAX]; RB_ENTRY(collundef) entry; } collundef_t; /* * These are called "chains" in libc. This records the fact that two * more characters should be treated as a single collating entity when * they appear together. For example, in Spanish gets collated * as a character between and . */ struct collelem { char *symbol; wchar_t *expand; int32_t ref[COLL_WEIGHTS_MAX]; RB_ENTRY(collelem) rb_bysymbol; RB_ENTRY(collelem) rb_byexpand; }; /* * Individual characters have a sequence of weights as well. */ typedef struct collchar { wchar_t wc; int32_t ref[COLL_WEIGHTS_MAX]; RB_ENTRY(collchar) entry; } collchar_t; /* * Substitution entries. The key is itself a priority. Note that * when we create one of these, we *automatically* wind up with a * fully resolved priority for the key, because creation of * substitutions creates a resolved priority at the same time. */ typedef struct subst{ int32_t key; int32_t ref[COLLATE_STR_LEN]; RB_ENTRY(subst) entry; RB_ENTRY(subst) entry_ref; } subst_t; static RB_HEAD(collsyms, collsym) collsyms; static RB_HEAD(collundefs, collundef) collundefs; static RB_HEAD(elem_by_symbol, collelem) elem_by_symbol; static RB_HEAD(elem_by_expand, collelem) elem_by_expand; static RB_HEAD(collchars, collchar) collchars; static RB_HEAD(substs, subst) substs[COLL_WEIGHTS_MAX]; static RB_HEAD(substs_ref, subst) substs_ref[COLL_WEIGHTS_MAX]; static RB_HEAD(weights, weight) weights[COLL_WEIGHTS_MAX]; static int32_t nweight[COLL_WEIGHTS_MAX]; /* * This is state tracking for the ellipsis token. Note that we start * the initial values so that the ellipsis logic will think we got a * magic starting value of NUL. It starts at minus one because the * starting point is exclusive -- i.e. the starting point is not * itself handled by the ellipsis code. */ static int currorder = EOF; static int lastorder = EOF; static collelem_t *currelem; static collchar_t *currchar; static collundef_t *currundef; static wchar_t ellipsis_start = 0; static int32_t ellipsis_weights[COLL_WEIGHTS_MAX]; /* * We keep a running tally of weights. */ static int nextpri = 1; static int nextsubst[COLL_WEIGHTS_MAX] = { 0 }; /* * This array collects up the weights for each level. */ static int32_t order_weights[COLL_WEIGHTS_MAX]; static int curr_weight = 0; static int32_t subst_weights[COLLATE_STR_LEN]; static int curr_subst = 0; /* * Some initial priority values. */ static int32_t pri_undefined[COLL_WEIGHTS_MAX]; static int32_t pri_ignore; static collate_info_t collinfo; static int32_t subst_count[COLL_WEIGHTS_MAX]; static int32_t chain_count; static int32_t large_count; static collpri_t *prilist = NULL; static int numpri = 0; static int maxpri = 0; static void start_order(int); static int32_t new_pri(void) { int i; if (numpri >= maxpri) { maxpri = maxpri ? maxpri * 2 : 1024; prilist = realloc(prilist, sizeof (collpri_t) * maxpri); if (prilist == NULL) { fprintf(stderr,"out of memory\n"); return (-1); } for (i = numpri; i < maxpri; i++) { prilist[i].res = UNKNOWN; prilist[i].pri = 0; prilist[i].pass = 0; } } return (numpri++); } static collpri_t * get_pri(int32_t ref) { if ((ref < 0) || (ref > numpri)) { INTERR; return (NULL); } return (&prilist[ref]); } static void set_pri(int32_t ref, int32_t v, res_t res) { collpri_t *pri; pri = get_pri(ref); if ((res == REFER) && ((v < 0) || (v >= numpri))) { INTERR; } /* Resolve self references */ if ((res == REFER) && (ref == v)) { v = nextpri; res = RESOLVED; } if (pri->res != UNKNOWN) { warn("repeated item in order list (first on %d)", pri->lineno); return; } pri->lineno = lineno; pri->pri = v; pri->res = res; } static int32_t resolve_pri(int32_t ref) { collpri_t *pri; static int32_t pass = 0; pri = get_pri(ref); pass++; while (pri->res == REFER) { if (pri->pass == pass) { /* report a line with the circular symbol */ lineno = pri->lineno; fprintf(stderr,"circular reference in order list\n"); return (-1); } if ((pri->pri < 0) || (pri->pri >= numpri)) { INTERR; return (-1); } pri->pass = pass; pri = &prilist[pri->pri]; } if (pri->res == UNKNOWN) { return (-1); } if (pri->res != RESOLVED) INTERR; return (pri->pri); } static int weight_compare(const void *n1, const void *n2) { int32_t k1 = ((const weight_t *)n1)->pri; int32_t k2 = ((const weight_t *)n2)->pri; return (k1 < k2 ? -1 : k1 > k2 ? 1 : 0); } RB_GENERATE_STATIC(weights, weight, entry, weight_compare); static int collsym_compare(const void *n1, const void *n2) { const collsym_t *c1 = n1; const collsym_t *c2 = n2; int rv; rv = strcmp(c1->name, c2->name); return ((rv < 0) ? -1 : (rv > 0) ? 1 : 0); } RB_GENERATE_STATIC(collsyms, collsym, entry, collsym_compare); static int collundef_compare(const void *n1, const void *n2) { const collundef_t *c1 = n1; const collundef_t *c2 = n2; int rv; rv = strcmp(c1->name, c2->name); return ((rv < 0) ? -1 : (rv > 0) ? 1 : 0); } RB_GENERATE_STATIC(collundefs, collundef, entry, collundef_compare); static int element_compare_symbol(const void *n1, const void *n2) { const collelem_t *c1 = n1; const collelem_t *c2 = n2; int rv; rv = strcmp(c1->symbol, c2->symbol); return ((rv < 0) ? -1 : (rv > 0) ? 1 : 0); } RB_GENERATE_STATIC(elem_by_symbol, collelem, rb_bysymbol, element_compare_symbol); static int element_compare_expand(const void *n1, const void *n2) { const collelem_t *c1 = n1; const collelem_t *c2 = n2; int rv; rv = wcscmp(c1->expand, c2->expand); return ((rv < 0) ? -1 : (rv > 0) ? 1 : 0); } RB_GENERATE_STATIC(elem_by_expand, collelem, rb_byexpand, element_compare_expand); static int collchar_compare(const void *n1, const void *n2) { wchar_t k1 = ((const collchar_t *)n1)->wc; wchar_t k2 = ((const collchar_t *)n2)->wc; return (k1 < k2 ? -1 : k1 > k2 ? 1 : 0); } RB_GENERATE_STATIC(collchars, collchar, entry, collchar_compare); static int subst_compare(const void *n1, const void *n2) { int32_t k1 = ((const subst_t *)n1)->key; int32_t k2 = ((const subst_t *)n2)->key; return (k1 < k2 ? -1 : k1 > k2 ? 1 : 0); } RB_GENERATE_STATIC(substs, subst, entry, subst_compare); static int subst_compare_ref(const void *n1, const void *n2) { const wchar_t *c1 = ((const subst_t *)n1)->ref; const wchar_t *c2 = ((const subst_t *)n2)->ref; int rv; rv = wcscmp(c1, c2); return ((rv < 0) ? -1 : (rv > 0) ? 1 : 0); } RB_GENERATE_STATIC(substs_ref, subst, entry_ref, subst_compare_ref); void init_collate(void) { int i; RB_INIT(&collsyms); RB_INIT(&collundefs); RB_INIT(&elem_by_symbol); RB_INIT(&elem_by_expand); RB_INIT(&collchars); for (i = 0; i < COLL_WEIGHTS_MAX; i++) { RB_INIT(&substs[i]); RB_INIT(&substs_ref[i]); RB_INIT(&weights[i]); nweight[i] = 1; } (void) memset(&collinfo, 0, sizeof (collinfo)); /* allocate some initial priorities */ pri_ignore = new_pri(); set_pri(pri_ignore, 0, RESOLVED); for (i = 0; i < COLL_WEIGHTS_MAX; i++) { pri_undefined[i] = new_pri(); /* we will override this later */ set_pri(pri_undefined[i], COLLATE_MAX_PRIORITY, UNKNOWN); } } void define_collsym(char *name) { collsym_t *sym; if ((sym = calloc(1, sizeof(*sym))) == NULL) { fprintf(stderr,"out of memory\n"); return; } sym->name = name; sym->ref = new_pri(); if (RB_FIND(collsyms, &collsyms, sym) != NULL) { /* * This should never happen because we are only called * for undefined symbols. */ free(sym); INTERR; return; } RB_INSERT(collsyms, &collsyms, sym); } collsym_t * lookup_collsym(char *name) { collsym_t srch; srch.name = name; return (RB_FIND(collsyms, &collsyms, &srch)); } collelem_t * lookup_collelem(char *symbol) { collelem_t srch; srch.symbol = symbol; return (RB_FIND(elem_by_symbol, &elem_by_symbol, &srch)); } static collundef_t * get_collundef(char *name) { collundef_t srch; collundef_t *ud; int i; srch.name = name; if ((ud = RB_FIND(collundefs, &collundefs, &srch)) == NULL) { if (((ud = calloc(1, sizeof(*ud))) == NULL) || ((ud->name = strdup(name)) == NULL)) { fprintf(stderr,"out of memory\n"); free(ud); return (NULL); } for (i = 0; i < NUM_WT; i++) { ud->ref[i] = new_pri(); } RB_INSERT(collundefs, &collundefs, ud); } add_charmap_undefined(name); return (ud); } static collchar_t * get_collchar(wchar_t wc, int create) { collchar_t srch; collchar_t *cc; int i; srch.wc = wc; cc = RB_FIND(collchars, &collchars, &srch); if ((cc == NULL) && create) { if ((cc = calloc(1, sizeof(*cc))) == NULL) { fprintf(stderr, "out of memory\n"); return (NULL); } for (i = 0; i < NUM_WT; i++) { cc->ref[i] = new_pri(); } cc->wc = wc; RB_INSERT(collchars, &collchars, cc); } return (cc); } void end_order_collsym(collsym_t *sym) { start_order(T_COLLSYM); /* update the weight */ set_pri(sym->ref, nextpri, RESOLVED); nextpri++; } void end_order(void) { int i; int32_t pri; int32_t ref; collpri_t *p; /* advance the priority/weight */ pri = nextpri; switch (currorder) { case T_CHAR: for (i = 0; i < NUM_WT; i++) { if (((ref = order_weights[i]) < 0) || ((p = get_pri(ref)) == NULL) || (p->pri == -1)) { /* unspecified weight is a self reference */ set_pri(currchar->ref[i], pri, RESOLVED); } else { set_pri(currchar->ref[i], ref, REFER); } order_weights[i] = -1; } /* leave a cookie trail in case next symbol is ellipsis */ ellipsis_start = currchar->wc + 1; currchar = NULL; break; case T_ELLIPSIS: /* save off the weights were we can find them */ for (i = 0; i < NUM_WT; i++) { ellipsis_weights[i] = order_weights[i]; order_weights[i] = -1; } break; case T_COLLELEM: if (currelem == NULL) { INTERR; } else { for (i = 0; i < NUM_WT; i++) { if (((ref = order_weights[i]) < 0) || ((p = get_pri(ref)) == NULL) || (p->pri == -1)) { set_pri(currelem->ref[i], pri, RESOLVED); } else { set_pri(currelem->ref[i], ref, REFER); } order_weights[i] = -1; } } break; case T_UNDEFINED: for (i = 0; i < NUM_WT; i++) { if (((ref = order_weights[i]) < 0) || ((p = get_pri(ref)) == NULL) || (p->pri == -1)) { set_pri(pri_undefined[i], -1, RESOLVED); } else { set_pri(pri_undefined[i], ref, REFER); } order_weights[i] = -1; } break; case T_SYMBOL: for (i = 0; i < NUM_WT; i++) { if (((ref = order_weights[i]) < 0) || ((p = get_pri(ref)) == NULL) || (p->pri == -1)) { set_pri(currundef->ref[i], pri, RESOLVED); } else { set_pri(currundef->ref[i], ref, REFER); } order_weights[i] = -1; } break; default: INTERR; } nextpri++; } static void start_order(int type) { int i; lastorder = currorder; currorder = type; /* this is used to protect ELLIPSIS processing */ if ((lastorder == T_ELLIPSIS) && (type != T_CHAR)) { fprintf(stderr, "character value expected\n"); } for (i = 0; i < COLL_WEIGHTS_MAX; i++) { order_weights[i] = -1; } curr_weight = 0; } void start_order_undefined(void) { start_order(T_UNDEFINED); } void start_order_symbol(char *name) { currundef = get_collundef(name); start_order(T_SYMBOL); } void start_order_char(wchar_t wc) { collchar_t *cc; int32_t ref; start_order(T_CHAR); /* * If we last saw an ellipsis, then we need to close the range. * Handle that here. Note that we have to be careful because the * items *inside* the range are treated exclusiveley to the items * outside of the range. The ends of the range can have quite * different weights than the range members. */ if (lastorder == T_ELLIPSIS) { int i; if (wc < ellipsis_start) { fprintf(stderr, "malformed range!\n"); return; } while (ellipsis_start < wc) { /* * pick all of the saved weights for the * ellipsis. note that -1 encodes for the * ellipsis itself, which means to take the * current relative priority. */ if ((cc = get_collchar(ellipsis_start, 1)) == NULL) { INTERR; return; } for (i = 0; i < NUM_WT; i++) { collpri_t *p; if (((ref = ellipsis_weights[i]) == -1) || ((p = get_pri(ref)) == NULL) || (p->pri == -1)) { set_pri(cc->ref[i], nextpri, RESOLVED); } else { set_pri(cc->ref[i], ref, REFER); } ellipsis_weights[i] = 0; } ellipsis_start++; nextpri++; } } currchar = get_collchar(wc, 1); } void start_order_collelem(collelem_t *e) { start_order(T_COLLELEM); currelem = e; } void start_order_ellipsis(void) { int i; start_order(T_ELLIPSIS); if (lastorder != T_CHAR) { fprintf(stderr, "illegal starting point for range\n"); return; } for (i = 0; i < NUM_WT; i++) { ellipsis_weights[i] = order_weights[i]; } } void define_collelem(char *name, wchar_t *wcs) { collelem_t *e; int i; if (wcslen(wcs) >= COLLATE_STR_LEN) { fprintf(stderr,"expanded collation element too long\n"); return; } if ((e = calloc(1, sizeof(*e))) == NULL) { fprintf(stderr, "out of memory\n"); return; } e->expand = wcs; e->symbol = name; /* * This is executed before the order statement, so we don't * know how many priorities we *really* need. We allocate one * for each possible weight. Not a big deal, as collating-elements * prove to be quite rare. */ for (i = 0; i < COLL_WEIGHTS_MAX; i++) { e->ref[i] = new_pri(); } /* A character sequence can only reduce to one element. */ if ((RB_FIND(elem_by_symbol, &elem_by_symbol, e) != NULL) || (RB_FIND(elem_by_expand, &elem_by_expand, e) != NULL)) { fprintf(stderr, "duplicate collating element definition\n"); free(e); return; } RB_INSERT(elem_by_symbol, &elem_by_symbol, e); RB_INSERT(elem_by_expand, &elem_by_expand, e); } void add_order_bit(int kw) { uint8_t bit = DIRECTIVE_UNDEF; switch (kw) { case T_FORWARD: bit = DIRECTIVE_FORWARD; break; case T_BACKWARD: bit = DIRECTIVE_BACKWARD; break; case T_POSITION: bit = DIRECTIVE_POSITION; break; default: INTERR; break; } collinfo.directive[collinfo.directive_count] |= bit; } void add_order_directive(void) { if (collinfo.directive_count >= COLL_WEIGHTS_MAX) { fprintf(stderr, "too many directives (max %d)\n", COLL_WEIGHTS_MAX); return; } collinfo.directive_count++; } static void add_order_pri(int32_t ref) { if (curr_weight >= NUM_WT) { fprintf(stderr, "too many weights (max %d)\n", NUM_WT); return; } order_weights[curr_weight] = ref; curr_weight++; } void add_order_collsym(collsym_t *s) { add_order_pri(s->ref); } void add_order_char(wchar_t wc) { collchar_t *cc; if ((cc = get_collchar(wc, 1)) == NULL) { INTERR; return; } add_order_pri(cc->ref[curr_weight]); } void add_order_collelem(collelem_t *e) { add_order_pri(e->ref[curr_weight]); } void add_order_ignore(void) { add_order_pri(pri_ignore); } void add_order_symbol(char *sym) { collundef_t *c; if ((c = get_collundef(sym)) == NULL) { INTERR; return; } add_order_pri(c->ref[curr_weight]); } void add_order_ellipsis(void) { /* special NULL value indicates self reference */ add_order_pri(0); } void add_order_subst(void) { subst_t srch; subst_t *s; int i; (void) memset(&srch, 0, sizeof (srch)); for (i = 0; i < curr_subst; i++) { srch.ref[i] = subst_weights[i]; subst_weights[i] = 0; } s = RB_FIND(substs_ref, &substs_ref[curr_weight], &srch); if (s == NULL) { if ((s = calloc(1, sizeof(*s))) == NULL) { fprintf(stderr,"out of memory\n"); return; } s->key = new_pri(); /* * We use a self reference for our key, but we set a * high bit to indicate that this is a substitution * reference. This will expedite table lookups later, * and prevent table lookups for situations that don't * require it. (In short, its a big win, because we * can skip a lot of binary searching.) */ set_pri(s->key, (nextsubst[curr_weight] | COLLATE_SUBST_PRIORITY), RESOLVED); nextsubst[curr_weight] += 1; for (i = 0; i < curr_subst; i++) { s->ref[i] = srch.ref[i]; } RB_INSERT(substs_ref, &substs_ref[curr_weight], s); if (RB_FIND(substs, &substs[curr_weight], s) != NULL) { INTERR; return; } RB_INSERT(substs, &substs[curr_weight], s); } curr_subst = 0; /* * We are using the current (unique) priority as a search key * in the substitution table. */ add_order_pri(s->key); } static void add_subst_pri(int32_t ref) { if (curr_subst >= COLLATE_STR_LEN) { fprintf(stderr,"substitution string is too long\n"); return; } subst_weights[curr_subst] = ref; curr_subst++; } void add_subst_char(wchar_t wc) { collchar_t *cc; if (((cc = get_collchar(wc, 1)) == NULL) || (cc->wc != wc)) { INTERR; return; } /* we take the weight for the character at that position */ add_subst_pri(cc->ref[curr_weight]); } void add_subst_collelem(collelem_t *e) { add_subst_pri(e->ref[curr_weight]); } void add_subst_collsym(collsym_t *s) { add_subst_pri(s->ref); } void add_subst_symbol(char *ptr) { collundef_t *cu; if ((cu = get_collundef(ptr)) != NULL) { add_subst_pri(cu->ref[curr_weight]); } } void add_weight(int32_t ref, int pass) { weight_t srch; weight_t *w; srch.pri = resolve_pri(ref); /* No translation of ignores */ if (srch.pri == 0) return; /* Substitution priorities are not weights */ if (srch.pri & COLLATE_SUBST_PRIORITY) return; if (RB_FIND(weights, &weights[pass], &srch) != NULL) return; if ((w = calloc(1, sizeof(*w))) == NULL) { fprintf(stderr, "out of memory\n"); return; } w->pri = srch.pri; RB_INSERT(weights, &weights[pass], w); } void add_weights(int32_t *refs) { int i; for (i = 0; i < NUM_WT; i++) { add_weight(refs[i], i); } } int32_t get_weight(int32_t ref, int pass) { weight_t srch; weight_t *w; int32_t pri; pri = resolve_pri(ref); if (pri & COLLATE_SUBST_PRIORITY) { return (pri); } if (pri <= 0) { return (pri); } srch.pri = pri; if ((w = RB_FIND(weights, &weights[pass], &srch)) == NULL) { INTERR; return (-1); } return (w->opt); } wchar_t * wsncpy(wchar_t *s1, const wchar_t *s2, size_t n) { wchar_t *os1 = s1; n++; while (--n > 0 && (*s1++ = htote(*s2++)) != 0) continue; if (n > 0) while (--n > 0) *s1++ = 0; return (os1); } #define RB_COUNT(x, name, head, cnt) do { \ (cnt) = 0; \ RB_FOREACH(x, name, (head)) { \ (cnt)++; \ } \ } while (0) #define RB_NUMNODES(type, name, head, cnt) do { \ type *t; \ cnt = 0; \ RB_FOREACH(t, name, head) { \ cnt++; \ } \ } while (0) void dump_collate(void) { FILE *f; int i, j, n; size_t sz; int32_t pri; collelem_t *ce; collchar_t *cc; subst_t *sb; char fmt_version[COLLATE_FMT_VERSION_LEN]; char def_version[XLOCALE_DEF_VERSION_LEN]; collate_char_t chars[UCHAR_MAX + 1]; collate_large_t *large; collate_subst_t *subst[COLL_WEIGHTS_MAX]; collate_chain_t *chain; /* * We have to run through a preliminary pass to identify all the * weights that we use for each sorting level. */ for (i = 0; i < NUM_WT; i++) { add_weight(pri_ignore, i); } for (i = 0; i < NUM_WT; i++) { RB_FOREACH(sb, substs, &substs[i]) { for (j = 0; sb->ref[j]; j++) { add_weight(sb->ref[j], i); } } } RB_FOREACH(ce, elem_by_expand, &elem_by_expand) { add_weights(ce->ref); } RB_FOREACH(cc, collchars, &collchars) { add_weights(cc->ref); } /* * Now we walk the entire set of weights, removing the gaps * in the weights. This gives us optimum usage. The walk * occurs in priority. */ for (i = 0; i < NUM_WT; i++) { weight_t *w; RB_FOREACH(w, weights, &weights[i]) { w->opt = nweight[i]; nweight[i] += 1; } } (void) memset(&chars, 0, sizeof (chars)); (void) memset(fmt_version, 0, COLLATE_FMT_VERSION_LEN); (void) strlcpy(fmt_version, COLLATE_FMT_VERSION, sizeof (fmt_version)); (void) memset(def_version, 0, XLOCALE_DEF_VERSION_LEN); if (version) (void) strlcpy(def_version, version, sizeof (def_version)); /* * We need to make sure we arrange for the UNDEFINED field * to show up. Also, set the total weight counts. */ for (i = 0; i < NUM_WT; i++) { if (resolve_pri(pri_undefined[i]) == -1) { set_pri(pri_undefined[i], -1, RESOLVED); /* they collate at the end of everything else */ collinfo.undef_pri[i] = htote(COLLATE_MAX_PRIORITY); } collinfo.pri_count[i] = htote(nweight[i]); } collinfo.pri_count[NUM_WT] = htote(max_wide()); collinfo.undef_pri[NUM_WT] = htote(COLLATE_MAX_PRIORITY); collinfo.directive[NUM_WT] = DIRECTIVE_UNDEFINED; /* * Ordinary character priorities */ for (i = 0; i <= UCHAR_MAX; i++) { if ((cc = get_collchar(i, 0)) != NULL) { for (j = 0; j < NUM_WT; j++) { chars[i].pri[j] = htote(get_weight(cc->ref[j], j)); } } else { for (j = 0; j < NUM_WT; j++) { chars[i].pri[j] = htote(get_weight(pri_undefined[j], j)); } /* * Per POSIX, for undefined characters, we * also have to add a last item, which is the * character code. */ chars[i].pri[NUM_WT] = htote(i); } } /* * Substitution tables */ for (i = 0; i < NUM_WT; i++) { collate_subst_t *st = NULL; subst_t *temp; RB_COUNT(temp, substs, &substs[i], n); subst_count[i] = n; if ((st = calloc(n, sizeof(collate_subst_t))) == NULL) { fprintf(stderr, "out of memory\n"); return; } n = 0; RB_FOREACH(sb, substs, &substs[i]) { if ((st[n].key = resolve_pri(sb->key)) < 0) { /* by definition these resolve! */ INTERR; } if (st[n].key != (n | COLLATE_SUBST_PRIORITY)) { INTERR; } st[n].key = htote(st[n].key); for (j = 0; sb->ref[j]; j++) { st[n].pri[j] = htote(get_weight(sb->ref[j], i)); } n++; } if (n != subst_count[i]) INTERR; subst[i] = st; } /* * Chains, i.e. collating elements */ RB_NUMNODES(collelem_t, elem_by_expand, &elem_by_expand, chain_count); chain = calloc(chain_count, sizeof(collate_chain_t)); if (chain == NULL) { fprintf(stderr, "out of memory\n"); return; } n = 0; RB_FOREACH(ce, elem_by_expand, &elem_by_expand) { (void) wsncpy(chain[n].str, ce->expand, COLLATE_STR_LEN); for (i = 0; i < NUM_WT; i++) { chain[n].pri[i] = htote(get_weight(ce->ref[i], i)); } n++; } if (n != chain_count) INTERR; /* * Large (> UCHAR_MAX) character priorities */ RB_NUMNODES(collchar_t, collchars, &collchars, n); large = calloc(n, sizeof(collate_large_t)); if (large == NULL) { fprintf(stderr, "out of memory\n"); return; } i = 0; RB_FOREACH(cc, collchars, &collchars) { int undef = 0; /* we already gathered those */ if (cc->wc <= UCHAR_MAX) continue; for (j = 0; j < NUM_WT; j++) { if ((pri = get_weight(cc->ref[j], j)) < 0) { undef = 1; } if (undef && (pri >= 0)) { /* if undefined, then all priorities are */ INTERR; } else { large[i].pri.pri[j] = htote(pri); } } if (!undef) { large[i].val = htote(cc->wc); large_count = i++; } } if ((f = open_category()) == NULL) { return; } /* Time to write the entire data set out */ for (i = 0; i < NUM_WT; i++) collinfo.subst_count[i] = htote(subst_count[i]); collinfo.chain_count = htote(chain_count); collinfo.large_count = htote(large_count); if ((wr_category(fmt_version, COLLATE_FMT_VERSION_LEN, f) < 0) || (wr_category(def_version, XLOCALE_DEF_VERSION_LEN, f) < 0) || (wr_category(&collinfo, sizeof (collinfo), f) < 0) || (wr_category(&chars, sizeof (chars), f) < 0)) { return; } for (i = 0; i < NUM_WT; i++) { sz = sizeof (collate_subst_t) * subst_count[i]; if (wr_category(subst[i], sz, f) < 0) { return; } } sz = sizeof (collate_chain_t) * chain_count; if (wr_category(chain, sz, f) < 0) { return; } sz = sizeof (collate_large_t) * large_count; if (wr_category(large, sz, f) < 0) { return; } close_category(f); } diff --git a/usr.bin/localedef/localedef.h b/usr.bin/localedef/localedef.h index 4b141ac8dc09..34299033214c 100644 --- a/usr.bin/localedef/localedef.h +++ b/usr.bin/localedef/localedef.h @@ -1,177 +1,177 @@ /*- * Copyright 2018 Nexenta Systems, Inc. * Copyright 2015 John Marino * * This source code is derived from the illumos localedef command, and * provided under BSD-style license terms by Nexenta Systems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. */ /* * POSIX localedef. */ /* Common header files. */ -#include + #include #include #include #include extern int com_char; extern int esc_char; extern int mb_cur_max; extern int mb_cur_min; extern int last_kw; extern int verbose; #if YYDEBUG extern int yydebug; #endif extern int lineno; extern int undefok; /* mostly ignore undefined symbols */ extern int warnok; extern int warnings; extern char *version; int yylex(void); void yyerror(const char *); _Noreturn void errf(const char *, ...) __printflike(1, 2); void warn(const char *, ...) __printflike(1, 2); int putl_category(const char *, FILE *); int wr_category(void *, size_t, FILE *); FILE *open_category(void); void close_category(FILE *); void copy_category(char *); const char *category_name(void); int get_category(void); int get_symbol(void); int get_escaped(int); int get_wide(void); void reset_scanner(const char *); void scan_to_eol(void); void add_wcs(wchar_t); void add_tok(int); wchar_t *get_wcs(void); uint32_t htote(uint32_t); /* charmap.c - CHARMAP handling */ void init_charmap(void); void add_charmap(const char *, int); void add_charmap_undefined(char *); void add_charmap_posix(void); void add_charmap_range(char *, char *, int); void add_charmap_char(const char *name, int val); int lookup_charmap(const char *, wchar_t *); int check_charmap_undefined(char *); int check_charmap(wchar_t); /* collate.o - LC_COLLATE handling */ typedef struct collelem collelem_t; typedef struct collsym collsym_t; void init_collate(void); void define_collsym(char *); void define_collelem(char *, wchar_t *); void add_order_directive(void); void add_order_bit(int); void dump_collate(void); collsym_t *lookup_collsym(char *); collelem_t *lookup_collelem(char *); void start_order_collelem(collelem_t *); void start_order_undefined(void); void start_order_symbol(char *); void start_order_char(wchar_t); void start_order_ellipsis(void); void end_order_collsym(collsym_t *); void end_order(void); void add_weight(int32_t, int); void add_weights(int32_t *); void add_weight_num(int); void add_order_collelem(collelem_t *); void add_order_collsym(collsym_t *); void add_order_char(wchar_t); void add_order_ignore(void); void add_order_ellipsis(void); void add_order_symbol(char *); void add_order_subst(void); void add_subst_char(wchar_t); void add_subst_collsym(collsym_t *); void add_subst_collelem(collelem_t *); void add_subst_symbol(char *); int32_t get_weight(int32_t, int); wchar_t * wsncpy(wchar_t *, const wchar_t *, size_t); /* ctype.c - LC_CTYPE handling */ void init_ctype(void); void add_ctype(int); void add_ctype_range(wchar_t); void add_width(int, int); void add_width_range(int, int, int); void add_caseconv(int, int); void dump_ctype(void); /* messages.c - LC_MESSAGES handling */ void init_messages(void); void add_message(wchar_t *); void dump_messages(void); /* monetary.c - LC_MONETARY handling */ void init_monetary(void); void add_monetary_str(wchar_t *); void add_monetary_num(int); void reset_monetary_group(void); void add_monetary_group(int); void dump_monetary(void); /* numeric.c - LC_NUMERIC handling */ void init_numeric(void); void add_numeric_str(wchar_t *); void reset_numeric_group(void); void add_numeric_group(int); void dump_numeric(void); /* time.c - LC_TIME handling */ void init_time(void); void add_time_str(wchar_t *); void reset_time_list(void); void add_time_list(wchar_t *); void check_time_list(void); void dump_time(void); /* wide.c - Wide character handling. */ int to_wide(wchar_t *, const char *); int to_mbs(char *, wchar_t); int to_mb(char *, wchar_t); char *to_mb_string(const wchar_t *); void set_wide_encoding(const char *); void werr(const char *, ...); const char *get_wide_encoding(void); int max_wide(void); //#define _(x) gettext(x) #define INTERR fprintf(stderr,"internal fault (%s:%d)\n", __FILE__, __LINE__) diff --git a/usr.bin/lockf/lockf.c b/usr.bin/lockf/lockf.c index dd02bf2a5417..7f88753d1743 100644 --- a/usr.bin/lockf/lockf.c +++ b/usr.bin/lockf/lockf.c @@ -1,353 +1,352 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 1997 John D. Polstra. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JOHN D. POLSTRA 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 JOHN D. POLSTRA OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FDLOCK_PREFIX "/dev/fd/" union lock_subject { long subj_fd; const char *subj_name; }; static int acquire_lock(union lock_subject *subj, int flags, int silent); static void cleanup(void); static void killed(int sig); static void timeout(int sig); static void usage(void) __dead2; static void wait_for_lock(const char *name); static const char *lockname; static int lockfd = -1; static int keep; static int fdlock; static volatile sig_atomic_t timed_out; /* * Check if fdlock is implied by the given `lockname`. We'll write the fd that * is represented by it out to ofd, and the caller is expected to do any * necessary validation on it. */ static int fdlock_implied(const char *name, long *ofd) { char *endp; long fd; if (strncmp(name, FDLOCK_PREFIX, sizeof(FDLOCK_PREFIX) - 1) != 0) return (0); /* Skip past the prefix. */ name += sizeof(FDLOCK_PREFIX) - 1; errno = 0; fd = strtol(name, &endp, 10); if (errno != 0 || *endp != '\0') return (0); *ofd = fd; return (1); } /* * Execute an arbitrary command while holding a file lock. */ int main(int argc, char **argv) { int ch, flags, silent, status; long long waitsec; pid_t child; union lock_subject subj; silent = keep = 0; flags = O_CREAT | O_RDONLY; waitsec = -1; /* Infinite. */ while ((ch = getopt(argc, argv, "knst:w")) != -1) { switch (ch) { case 'k': keep = 1; break; case 'n': flags &= ~O_CREAT; break; case 's': silent = 1; break; case 't': { const char *errstr; waitsec = strtonum(optarg, 0, UINT_MAX, &errstr); if (errstr != NULL) errx(EX_USAGE, "invalid timeout \"%s\"", optarg); } break; case 'w': flags = (flags & ~O_RDONLY) | O_WRONLY; break; default: usage(); } } argc -= optind; argv += optind; if (argc == 0) usage(); lockname = argv[0]; argc--; argv++; /* * If there aren't any arguments left, then we must be in fdlock mode. */ if (argc == 0 && *lockname != '/') { fdlock = 1; subj.subj_fd = -1; } else { fdlock = fdlock_implied(lockname, &subj.subj_fd); if (argc == 0 && !fdlock) { fprintf(stderr, "Expected fd, got '%s'\n", lockname); usage(); } } if (fdlock) { if (subj.subj_fd < 0) { char *endp; errno = 0; subj.subj_fd = strtol(lockname, &endp, 10); if (errno != 0 || *endp != '\0') { fprintf(stderr, "Expected fd, got '%s'\n", lockname); usage(); } } if (subj.subj_fd < 0 || subj.subj_fd > INT_MAX) { fprintf(stderr, "fd '%ld' out of range\n", subj.subj_fd); usage(); } } else { subj.subj_name = lockname; } if (waitsec > 0) { /* Set up a timeout. */ struct sigaction act; act.sa_handler = timeout; sigemptyset(&act.sa_mask); act.sa_flags = 0; /* Note that we do not set SA_RESTART. */ sigaction(SIGALRM, &act, NULL); alarm((unsigned int)waitsec); } /* * If the "-k" option is not given, then we must not block when * acquiring the lock. If we did, then the lock holder would * unlink the file upon releasing the lock, and we would acquire * a lock on a file with no directory entry. Then another * process could come along and acquire the same lock. To avoid * this problem, we separate out the actions of waiting for the * lock to be available and of actually acquiring the lock. * * That approach produces behavior that is technically correct; * however, it causes some performance & ordering problems for * locks that have a lot of contention. First, it is unfair in * the sense that a released lock isn't necessarily granted to * the process that has been waiting the longest. A waiter may * be starved out indefinitely. Second, it creates a thundering * herd situation each time the lock is released. * * When the "-k" option is used, the unlink race no longer * exists. In that case we can block while acquiring the lock, * avoiding the separate step of waiting for the lock. This * yields fairness and improved performance. */ lockfd = acquire_lock(&subj, flags | O_NONBLOCK, silent); while (lockfd == -1 && !timed_out && waitsec != 0) { if (keep || fdlock) lockfd = acquire_lock(&subj, flags, silent); else { wait_for_lock(lockname); lockfd = acquire_lock(&subj, flags | O_NONBLOCK, silent); } } if (waitsec > 0) alarm(0); if (lockfd == -1) { /* We failed to acquire the lock. */ if (silent) exit(EX_TEMPFAIL); errx(EX_TEMPFAIL, "%s: already locked", lockname); } /* At this point, we own the lock. */ /* Nothing else to do for FD lock, just exit */ if (argc == 0) { assert(fdlock); return 0; } if (atexit(cleanup) == -1) err(EX_OSERR, "atexit failed"); if ((child = fork()) == -1) err(EX_OSERR, "cannot fork"); if (child == 0) { /* The child process. */ close(lockfd); execvp(argv[0], argv); warn("%s", argv[0]); _exit(1); } /* This is the parent process. */ signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTERM, killed); fclose(stdin); fclose(stdout); fclose(stderr); if (waitpid(child, &status, 0) == -1) exit(EX_OSERR); return (WIFEXITED(status) ? WEXITSTATUS(status) : EX_SOFTWARE); } /* * Try to acquire a lock on the given file/fd, creating the file if * necessary. The flags argument is O_NONBLOCK or 0, depending on * whether we should wait for the lock. Returns an open file descriptor * on success, or -1 on failure. */ static int acquire_lock(union lock_subject *subj, int flags, int silent) { int fd; if (fdlock) { assert(subj->subj_fd >= 0 && subj->subj_fd <= INT_MAX); fd = (int)subj->subj_fd; if (flock(fd, LOCK_EX | LOCK_NB) == -1) { if (errno == EAGAIN || errno == EINTR) return (-1); err(EX_CANTCREAT, "cannot lock fd %d", fd); } } else if ((fd = open(subj->subj_name, O_EXLOCK|flags, 0666)) == -1) { if (errno == EAGAIN || errno == EINTR) return (-1); else if (errno == ENOENT && (flags & O_CREAT) == 0) { if (!silent) warn("%s", subj->subj_name); exit(EX_UNAVAILABLE); } err(EX_CANTCREAT, "cannot open %s", subj->subj_name); } return (fd); } /* * Remove the lock file. */ static void cleanup(void) { if (keep || fdlock) flock(lockfd, LOCK_UN); else unlink(lockname); } /* * Signal handler for SIGTERM. Cleans up the lock file, then re-raises * the signal. */ static void killed(int sig) { cleanup(); signal(sig, SIG_DFL); if (kill(getpid(), sig) == -1) _Exit(EX_OSERR); } /* * Signal handler for SIGALRM. */ static void timeout(int sig __unused) { timed_out = 1; } static void usage(void) { fprintf(stderr, "usage: lockf [-knsw] [-t seconds] file command [arguments]\n" " lockf [-s] [-t seconds] fd\n"); exit(EX_USAGE); } /* * Wait until it might be possible to acquire a lock on the given file. * If the file does not exist, return immediately without creating it. */ static void wait_for_lock(const char *name) { int fd; if ((fd = open(name, O_RDONLY|O_EXLOCK, 0666)) == -1) { if (errno == ENOENT || errno == EINTR) return; err(EX_CANTCREAT, "cannot open %s", name); } close(fd); } diff --git a/usr.bin/login/login.c b/usr.bin/login/login.c index 3d473a3d3274..409e6cc373ad 100644 --- a/usr.bin/login/login.c +++ b/usr.bin/login/login.c @@ -1,1033 +1,1030 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * Copyright (c) 2002 Networks Associates Technologies, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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 -#endif - #include /* * login [ name ] * login -h hostname (for telnetd, etc.) * login -f name (for pre-authenticated login: datakit, xterm, etc.) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "login.h" #include "pathnames.h" static int auth_pam(void); static void bail(int, int); static void bail_internal(int, int, int); static int export(const char *); static void export_pam_environment(void); static int motd(const char *); static void badlogin(char *); static char *getloginname(void); static void pam_syslog(const char *); static void pam_cleanup(void); static void refused(const char *, const char *, int); static const char *stypeof(char *); static void sigint(int); static void timedout(int); static void bail_sig(int); static void usage(void); #define TTYGRPNAME "tty" /* group to own ttys */ #define DEFAULT_BACKOFF 3 #define DEFAULT_RETRIES 10 #define DEFAULT_PROMPT "login: " #define DEFAULT_PASSWD_PROMPT "Password:" #define TERM_UNKNOWN "su" #define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ #define NO_SLEEP_EXIT 0 #define SLEEP_EXIT 5 /* * This bounds the time given to login. Not a define so it can * be patched on machines where it's too small. */ static u_int timeout = 300; /* Buffer for signal handling of timeout */ static jmp_buf timeout_buf; struct passwd *pwd; static int failures; static char *envinit[1]; /* empty environment list */ /* * Command line flags and arguments */ static int fflag; /* -f: do not perform authentication */ static int hflag; /* -h: login from remote host */ static char *hostname; /* hostname from command line */ static int pflag; /* -p: preserve environment */ /* * User name */ static char *username; /* user name */ static char *olduser; /* previous user name */ /* * Prompts */ static char default_prompt[] = DEFAULT_PROMPT; static const char *prompt; static char default_passwd_prompt[] = DEFAULT_PASSWD_PROMPT; static const char *passwd_prompt; static char *tty; /* * PAM data */ static pam_handle_t *pamh = NULL; static struct pam_conv pamc = { openpam_ttyconv, NULL }; static int pam_err; static int pam_silent = PAM_SILENT; static int pam_cred_established; static int pam_session_established; int main(int argc, char *argv[]) { struct group *gr; struct stat st; int retries, backoff; int ask, ch, cnt, quietlog, rootlogin, rval; uid_t uid, euid; gid_t egid; char *term; char *p, *ttyn; char tname[sizeof(_PATH_TTY) + 10]; char *arg0; const char *tp; const char *shell = NULL; login_cap_t *lc = NULL; login_cap_t *lc_user = NULL; pid_t pid; sigset_t mask, omask; struct sigaction sa; #ifdef USE_BSM_AUDIT char auditsuccess = 1; #endif sa.sa_flags = SA_RESTART; (void)sigfillset(&sa.sa_mask); sa.sa_handler = SIG_IGN; (void)sigaction(SIGQUIT, &sa, NULL); (void)sigaction(SIGINT, &sa, NULL); (void)sigaction(SIGHUP, &sa, NULL); if (setjmp(timeout_buf)) { if (failures) badlogin(username); (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout); bail(NO_SLEEP_EXIT, 0); } sa.sa_handler = timedout; (void)sigaction(SIGALRM, &sa, NULL); (void)alarm(timeout); (void)setpriority(PRIO_PROCESS, 0, 0); openlog("login", LOG_CONS, LOG_AUTH); uid = getuid(); euid = geteuid(); egid = getegid(); while ((ch = getopt(argc, argv, "fh:p")) != -1) switch (ch) { case 'f': fflag = 1; break; case 'h': if (uid != 0) errx(1, "-h option: %s", strerror(EPERM)); if (strlen(optarg) >= MAXHOSTNAMELEN) errx(1, "-h option: %s: exceeds maximum " "hostname size", optarg); hflag = 1; hostname = optarg; break; case 'p': pflag = 1; break; case '?': default: if (uid == 0) syslog(LOG_ERR, "invalid flag %c", ch); usage(); } argc -= optind; argv += optind; if (argc > 0) { username = strdup(*argv); if (username == NULL) err(1, "strdup()"); ask = 0; } else { ask = 1; } setproctitle("-%s", getprogname()); closefrom(3); /* * Get current TTY */ ttyn = ttyname(STDIN_FILENO); if (ttyn == NULL || *ttyn == '\0') { (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); ttyn = tname; } if (strncmp(ttyn, _PATH_DEV, sizeof _PATH_DEV - 1) == 0) tty = ttyn + sizeof _PATH_DEV - 1; else tty = ttyn; /* * Get "login-retries" & "login-backoff" from default class */ lc = login_getclass(NULL); prompt = login_getcapstr(lc, "login_prompt", default_prompt, default_prompt); passwd_prompt = login_getcapstr(lc, "passwd_prompt", default_passwd_prompt, default_passwd_prompt); retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES, DEFAULT_RETRIES); backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF, DEFAULT_BACKOFF); login_close(lc); lc = NULL; /* * Try to authenticate the user until we succeed or time out. */ for (cnt = 0;; ask = 1) { if (ask) { fflag = 0; if (olduser != NULL) free(olduser); olduser = username; username = getloginname(); } rootlogin = 0; /* * Note if trying multiple user names; log failures for * previous user name, but don't bother logging one failure * for nonexistent name (mistyped username). */ if (failures && strcmp(olduser, username) != 0) { if (failures > (pwd ? 0 : 1)) badlogin(olduser); } /* * Load the PAM policy and set some variables */ pam_err = pam_start("login", username, &pamc, &pamh); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_start()"); #ifdef USE_BSM_AUDIT au_login_fail("PAM Error", 1); #endif bail(NO_SLEEP_EXIT, 1); } pam_err = pam_set_item(pamh, PAM_TTY, tty); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_set_item(PAM_TTY)"); #ifdef USE_BSM_AUDIT au_login_fail("PAM Error", 1); #endif bail(NO_SLEEP_EXIT, 1); } pam_err = pam_set_item(pamh, PAM_RHOST, hostname); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_set_item(PAM_RHOST)"); #ifdef USE_BSM_AUDIT au_login_fail("PAM Error", 1); #endif bail(NO_SLEEP_EXIT, 1); } pwd = getpwnam(username); if (pwd != NULL && pwd->pw_uid == 0) rootlogin = 1; /* * If the -f option was specified and the caller is * root or the caller isn't changing their uid, don't * authenticate. */ if (pwd != NULL && fflag && (uid == (uid_t)0 || uid == (uid_t)pwd->pw_uid)) { /* already authenticated */ rval = 0; #ifdef USE_BSM_AUDIT auditsuccess = 0; /* opened a terminal window only */ #endif } else { fflag = 0; (void)setpriority(PRIO_PROCESS, 0, -4); rval = auth_pam(); (void)setpriority(PRIO_PROCESS, 0, 0); } if (pwd && rval == 0) break; pam_cleanup(); /* * We are not exiting here, but this corresponds to a failed * login event, so set exitstatus to 1. */ #ifdef USE_BSM_AUDIT au_login_fail("Login incorrect", 1); #endif (void)printf("Login incorrect\n"); failures++; pwd = NULL; /* * Allow up to 'retry' (10) attempts, but start * backing off after 'backoff' (3) attempts. */ if (++cnt > backoff) { if (cnt >= retries) { badlogin(username); bail(SLEEP_EXIT, 1); } sleep((u_int)((cnt - backoff) * 5)); } } /* committed to login -- turn off timeout */ (void)alarm((u_int)0); (void)sigemptyset(&mask); (void)sigaddset(&mask, SIGHUP); (void)sigaddset(&mask, SIGTERM); (void)sigprocmask(SIG_BLOCK, &mask, &omask); sa.sa_handler = bail_sig; (void)sigaction(SIGHUP, &sa, NULL); (void)sigaction(SIGTERM, &sa, NULL); endpwent(); #ifdef USE_BSM_AUDIT /* Audit successful login. */ if (auditsuccess) au_login_success(); #endif /* * This needs to happen before login_getpwclass to support * home directories on GSS-API authenticated NFS where the * kerberos credentials need to be saved so that the kernel * can authenticate to the NFS server. */ pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_setcred()"); bail(NO_SLEEP_EXIT, 1); } pam_cred_established = 1; /* * Establish the login class. */ lc = login_getpwclass(pwd); lc_user = login_getuserclass(pwd); if (!(quietlog = login_getcapbool(lc_user, "hushlogin", 0))) quietlog = login_getcapbool(lc, "hushlogin", 0); /* * Switching needed for NFS with root access disabled. * * XXX: This change fails to modify the additional groups for the * process, and as such, may restrict rights normally granted * through those groups. */ (void)setegid(pwd->pw_gid); (void)seteuid(rootlogin ? 0 : pwd->pw_uid); if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) { if (login_getcapbool(lc, "requirehome", 0)) refused("Home directory not available", "HOMEDIR", 1); if (chdir("/") < 0) refused("Cannot find root directory", "ROOTDIR", 1); if (!quietlog || *pwd->pw_dir) printf("No home directory.\nLogging in with home = \"/\".\n"); pwd->pw_dir = strdup("/"); if (pwd->pw_dir == NULL) { syslog(LOG_NOTICE, "strdup(): %m"); bail(SLEEP_EXIT, 1); } } (void)seteuid(euid); (void)setegid(egid); if (!quietlog) { quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; if (!quietlog) pam_silent = 0; } shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell); if (*pwd->pw_shell == '\0') pwd->pw_shell = strdup(_PATH_BSHELL); if (pwd->pw_shell == NULL) { syslog(LOG_NOTICE, "strdup(): %m"); bail(SLEEP_EXIT, 1); } if (*shell == '\0') /* Not overridden */ shell = pwd->pw_shell; if ((shell = strdup(shell)) == NULL) { syslog(LOG_NOTICE, "strdup(): %m"); bail(SLEEP_EXIT, 1); } /* * Set device protections, depending on what terminal the * user is logged in. This feature is used on Suns to give * console users better privacy. */ login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); /* * Clear flags of the tty. None should be set, and when the * user sets them otherwise, this can cause the chown to fail. * Since it isn't clear that flags are useful on character * devices, we just clear them. * * We don't log in the case of EOPNOTSUPP because dev might be * on NFS, which doesn't support chflags. * * We don't log in the EROFS because that means that /dev is on * a read only file system and we assume that the permissions there * are sane. */ if (ttyn != tname && chflags(ttyn, 0)) if (errno != EOPNOTSUPP && errno != EROFS) syslog(LOG_ERR, "chflags(%s): %m", ttyn); if (ttyn != tname && chown(ttyn, pwd->pw_uid, (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid)) if (errno != EROFS) syslog(LOG_ERR, "chown(%s): %m", ttyn); #ifdef LOGALL /* * Syslog each successful login, so we don't have to watch * hundreds of wtmp or lastlogin files. */ if (hflag) syslog(LOG_INFO, "login from %s on %s as %s", hostname, tty, pwd->pw_name); else syslog(LOG_INFO, "login on %s as %s", tty, pwd->pw_name); #endif /* * If fflag is on, assume caller/authenticator has logged root * login. */ if (rootlogin && fflag == 0) { if (hflag) syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s", username, tty, hostname); else syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty); } /* * Destroy environment unless user has requested its * preservation - but preserve TERM in all cases */ term = getenv("TERM"); if (!pflag) environ = envinit; if (term != NULL) setenv("TERM", term, 0); /* * PAM modules might add supplementary groups during pam_setcred(). */ if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { syslog(LOG_ERR, "setusercontext() failed - exiting"); bail(NO_SLEEP_EXIT, 1); } pam_err = pam_setcred(pamh, pam_silent|PAM_REINITIALIZE_CRED); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_setcred()"); bail(NO_SLEEP_EXIT, 1); } pam_err = pam_open_session(pamh, pam_silent); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_open_session()"); bail(NO_SLEEP_EXIT, 1); } pam_session_established = 1; /* * We must fork() before setuid() because we need to call * pam_close_session() as root. */ pid = fork(); if (pid < 0) { err(1, "fork"); } else if (pid != 0) { /* * Parent: wait for child to finish, then clean up * session. * * If we get SIGHUP or SIGTERM, clean up the session * and exit right away. This will make the terminal * inaccessible and send SIGHUP to the foreground * process group. */ int status; setproctitle("-%s [pam]", getprogname()); (void)sigprocmask(SIG_SETMASK, &omask, NULL); waitpid(pid, &status, 0); (void)sigprocmask(SIG_BLOCK, &mask, NULL); bail(NO_SLEEP_EXIT, 0); } /* * NOTICE: We are now in the child process! */ /* * Add any environment variables the PAM modules may have set. */ export_pam_environment(); /* * We're done with PAM now; our parent will deal with the rest. */ pam_end(pamh, 0); pamh = NULL; /* * We don't need to be root anymore, so set the login name and * the UID. */ if (setlogin(username) != 0) { syslog(LOG_ERR, "setlogin(%s): %m - exiting", username); bail(NO_SLEEP_EXIT, 1); } if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) { syslog(LOG_ERR, "setusercontext() failed - exiting"); exit(1); } (void)setenv("SHELL", pwd->pw_shell, 1); (void)setenv("HOME", pwd->pw_dir, 1); /* Overwrite "term" from login.conf(5) for any known TERM */ if (term == NULL && (tp = stypeof(tty)) != NULL) (void)setenv("TERM", tp, 1); else (void)setenv("TERM", TERM_UNKNOWN, 0); (void)setenv("LOGNAME", username, 1); (void)setenv("USER", username, 1); (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0); if (!quietlog) { const char *cw; cw = login_getcapstr(lc, "welcome", NULL, NULL); if (cw != NULL && access(cw, F_OK) == 0) motd(cw); else motd(_PATH_MOTDFILE); if (login_getcapbool(lc_user, "nocheckmail", 0) == 0 && login_getcapbool(lc, "nocheckmail", 0) == 0) { char *cx; /* $MAIL may have been set by class. */ cx = getenv("MAIL"); if (cx == NULL) { asprintf(&cx, "%s/%s", _PATH_MAILDIR, pwd->pw_name); } if (cx && stat(cx, &st) == 0 && st.st_size != 0) (void)printf("You have %smail.\n", (st.st_mtime > st.st_atime) ? "new " : ""); if (getenv("MAIL") == NULL) free(cx); } } login_close(lc_user); login_close(lc); sa.sa_handler = SIG_DFL; (void)sigaction(SIGALRM, &sa, NULL); (void)sigaction(SIGQUIT, &sa, NULL); (void)sigaction(SIGINT, &sa, NULL); (void)sigaction(SIGTERM, &sa, NULL); (void)sigaction(SIGHUP, &sa, NULL); sa.sa_handler = SIG_IGN; (void)sigaction(SIGTSTP, &sa, NULL); (void)sigprocmask(SIG_SETMASK, &omask, NULL); /* * Login shells have a leading '-' in front of argv[0] */ p = strrchr(pwd->pw_shell, '/'); if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) { syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size", username); errx(1, "shell exceeds maximum pathname size"); } else if (arg0 == NULL) { err(1, "asprintf()"); } execlp(shell, arg0, (char *)0); err(1, "%s", shell); /* * That's it, folks! */ } /* * Attempt to authenticate the user using PAM. Returns 0 if the user is * authenticated, or 1 if not authenticated. If some sort of PAM system * error occurs (e.g., the "/etc/pam.conf" file is missing) then this * function returns -1. This can be used as an indication that we should * fall back to a different authentication mechanism. */ static int auth_pam(void) { const char *tmpl_user; const void *item; int rval; pam_err = pam_authenticate(pamh, pam_silent); switch (pam_err) { case PAM_SUCCESS: /* * With PAM we support the concept of a "template" * user. The user enters a login name which is * authenticated by PAM, usually via a remote service * such as RADIUS or TACACS+. If authentication * succeeds, a different but related "template" name * is used for setting the credentials, shell, and * home directory. The name the user enters need only * exist on the remote authentication server, but the * template name must be present in the local password * database. * * This is supported by two various mechanisms in the * individual modules. However, from the application's * point of view, the template user is always passed * back as a changed value of the PAM_USER item. */ pam_err = pam_get_item(pamh, PAM_USER, &item); if (pam_err == PAM_SUCCESS) { tmpl_user = (const char *)item; if (strcmp(username, tmpl_user) != 0) pwd = getpwnam(tmpl_user); } else { pam_syslog("pam_get_item(PAM_USER)"); } rval = 0; break; case PAM_AUTH_ERR: case PAM_USER_UNKNOWN: case PAM_MAXTRIES: rval = 1; break; default: pam_syslog("pam_authenticate()"); rval = -1; break; } if (rval == 0) { pam_err = pam_acct_mgmt(pamh, pam_silent); switch (pam_err) { case PAM_SUCCESS: break; case PAM_NEW_AUTHTOK_REQD: pam_err = pam_chauthtok(pamh, pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_chauthtok()"); rval = 1; } break; default: pam_syslog("pam_acct_mgmt()"); rval = 1; break; } } if (rval != 0) { pam_end(pamh, pam_err); pamh = NULL; } return (rval); } /* * Export any environment variables PAM modules may have set */ static void export_pam_environment(void) { char **pam_env; char **pp; pam_env = pam_getenvlist(pamh); if (pam_env != NULL) { for (pp = pam_env; *pp != NULL; pp++) { (void)export(*pp); free(*pp); } } } /* * Perform sanity checks on an environment variable: * - Make sure there is an '=' in the string. * - Make sure the string doesn't run on too long. * - Do not export certain variables. This list was taken from the * Solaris pam_putenv(3) man page. * Then export it. */ static int export(const char *s) { static const char *noexport[] = { "SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH", "IFS", "PATH", NULL }; char *p; const char **pp; size_t n; int rv; if (strlen(s) > 1024 || (p = strchr(s, '=')) == NULL) return (0); if (strncmp(s, "LD_", 3) == 0) return (0); for (pp = noexport; *pp != NULL; pp++) { n = strlen(*pp); if (s[n] == '=' && strncmp(s, *pp, n) == 0) return (0); } *p = '\0'; rv = setenv(s, p + 1, 1); *p = '='; if (rv == -1) return (0); return (1); } static void usage(void) { (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n"); exit(1); } /* * Prompt user and read login name from stdin. */ static char * getloginname(void) { char *nbuf, *p; int ch; nbuf = malloc(MAXLOGNAME); if (nbuf == NULL) err(1, "malloc()"); do { (void)printf("%s", prompt); for (p = nbuf; (ch = getchar()) != '\n'; ) { if (ch == EOF) { badlogin(username); bail(NO_SLEEP_EXIT, 0); } if (p < nbuf + MAXLOGNAME - 1) *p++ = ch; } } while (p == nbuf); *p = '\0'; if (nbuf[0] == '-') { pam_silent = 0; memmove(nbuf, nbuf + 1, strlen(nbuf)); } else { pam_silent = PAM_SILENT; } return nbuf; } /* * SIGINT handler for motd(). */ static volatile int motdinterrupt; static void sigint(int signo __unused) { motdinterrupt = 1; } /* * Display the contents of a file (such as /etc/motd). */ static int motd(const char *motdfile) { struct sigaction newint, oldint; FILE *f; int ch; if ((f = fopen(motdfile, "r")) == NULL) return (-1); motdinterrupt = 0; newint.sa_handler = sigint; newint.sa_flags = 0; sigfillset(&newint.sa_mask); sigaction(SIGINT, &newint, &oldint); while ((ch = fgetc(f)) != EOF && !motdinterrupt) putchar(ch); sigaction(SIGINT, &oldint, NULL); if (ch != EOF || ferror(f)) { fclose(f); return (-1); } fclose(f); return (0); } /* * SIGALRM handler, to enforce login prompt timeout. * * XXX This can potentially confuse the hell out of PAM. We should * XXX instead implement a conversation function that returns * XXX PAM_CONV_ERR when interrupted by a signal, and have the signal * XXX handler just set a flag. */ static void timedout(int signo __unused) { longjmp(timeout_buf, signo); } static void badlogin(char *name) { if (failures == 0) return; if (hflag) { syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s", failures, failures > 1 ? "S" : "", hostname); syslog(LOG_AUTHPRIV|LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s, %s", failures, failures > 1 ? "S" : "", hostname, name); } else { syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s", failures, failures > 1 ? "S" : "", tty); syslog(LOG_AUTHPRIV|LOG_NOTICE, "%d LOGIN FAILURE%s ON %s, %s", failures, failures > 1 ? "S" : "", tty, name); } failures = 0; } const char * stypeof(char *ttyid) { struct ttyent *t; if (ttyid != NULL && *ttyid != '\0') { t = getttynam(ttyid); if (t != NULL && t->ty_type != NULL) return (t->ty_type); } return (NULL); } static void refused(const char *msg, const char *rtype, int lout) { if (msg != NULL) printf("%s.\n", msg); if (hflag) syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s", pwd->pw_name, rtype, hostname, tty); else syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s", pwd->pw_name, rtype, tty); if (lout) bail(SLEEP_EXIT, 1); } /* * Log a PAM error */ static void pam_syslog(const char *msg) { syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err)); } /* * Shut down PAM */ static void pam_cleanup(void) { if (pamh != NULL) { if (pam_session_established) { pam_err = pam_close_session(pamh, 0); if (pam_err != PAM_SUCCESS) pam_syslog("pam_close_session()"); } pam_session_established = 0; if (pam_cred_established) { pam_err = pam_setcred(pamh, pam_silent|PAM_DELETE_CRED); if (pam_err != PAM_SUCCESS) pam_syslog("pam_setcred()"); } pam_cred_established = 0; pam_end(pamh, pam_err); pamh = NULL; } } static void bail_internal(int sec, int eval, int signo) { struct sigaction sa; pam_cleanup(); #ifdef USE_BSM_AUDIT if (pwd != NULL) audit_logout(); #endif (void)sleep(sec); if (signo == 0) exit(eval); else { sa.sa_handler = SIG_DFL; sa.sa_flags = 0; (void)sigemptyset(&sa.sa_mask); (void)sigaction(signo, &sa, NULL); (void)sigaddset(&sa.sa_mask, signo); (void)sigprocmask(SIG_UNBLOCK, &sa.sa_mask, NULL); raise(signo); exit(128 + signo); } } /* * Exit, optionally after sleeping a few seconds */ static void bail(int sec, int eval) { bail_internal(sec, eval, 0); } /* * Exit because of a signal. * This is not async-signal safe, so only call async-signal safe functions * while the signal is unmasked. */ static void bail_sig(int signo) { bail_internal(NO_SLEEP_EXIT, 0, signo); } diff --git a/usr.bin/login/login_audit.c b/usr.bin/login/login_audit.c index 59b182e19de3..1f2ec4d80cc9 100644 --- a/usr.bin/login/login_audit.c +++ b/usr.bin/login/login_audit.c @@ -1,204 +1,203 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2005 Apple Computer, Inc. * All rights reserved. * * @APPLE_BSD_LICENSE_HEADER_START@ * * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. * * @APPLE_BSD_LICENSE_HEADER_END@ */ -#include #include #include #include #include #include #include #include #include #include #include "login.h" /* * Audit data */ static au_tid_t tid; /* * The following tokens are included in the audit record for a successful * login: header, subject, return. */ void au_login_success(void) { token_t *tok; int aufd; au_mask_t aumask; auditinfo_t auinfo; uid_t uid = pwd->pw_uid; gid_t gid = pwd->pw_gid; pid_t pid = getpid(); int au_cond; /* If we are not auditing, don't cut an audit record; just return. */ if (auditon(A_GETCOND, &au_cond, sizeof(au_cond)) < 0) { if (errno == ENOSYS) return; errx(1, "could not determine audit condition"); } if (au_cond == AUC_NOAUDIT) return; /* Compute and set the user's preselection mask. */ if (au_user_mask(pwd->pw_name, &aumask) == -1) errx(1, "could not calculate audit mask"); /* Set the audit info for the user. */ auinfo.ai_auid = uid; auinfo.ai_asid = pid; bcopy(&tid, &auinfo.ai_termid, sizeof(auinfo.ai_termid)); bcopy(&aumask, &auinfo.ai_mask, sizeof(auinfo.ai_mask)); if (setaudit(&auinfo) != 0) err(1, "setaudit failed"); if ((aufd = au_open()) == -1) errx(1, "audit error: au_open() failed"); if ((tok = au_to_subject32(uid, geteuid(), getegid(), uid, gid, pid, pid, &tid)) == NULL) errx(1, "audit error: au_to_subject32() failed"); au_write(aufd, tok); if ((tok = au_to_return32(0, 0)) == NULL) errx(1, "audit error: au_to_return32() failed"); au_write(aufd, tok); if (au_close(aufd, 1, AUE_login) == -1) errx(1, "audit record was not committed."); } /* * The following tokens are included in the audit record for failed * login attempts: header, subject, text, return. */ void au_login_fail(const char *errmsg, int na) { token_t *tok; int aufd; int au_cond; uid_t uid; gid_t gid; pid_t pid = getpid(); /* If we are not auditing, don't cut an audit record; just return. */ if (auditon(A_GETCOND, &au_cond, sizeof(au_cond)) < 0) { if (errno == ENOSYS) return; errx(1, "could not determine audit condition"); } if (au_cond == AUC_NOAUDIT) return; if ((aufd = au_open()) == -1) errx(1, "audit error: au_open() failed"); if (na) { /* * Non attributable event. Assuming that login is not called * within a user's session => auid,asid == -1. */ if ((tok = au_to_subject32(-1, geteuid(), getegid(), -1, -1, pid, -1, &tid)) == NULL) errx(1, "audit error: au_to_subject32() failed"); } else { /* We know the subject -- so use its value instead. */ uid = pwd->pw_uid; gid = pwd->pw_gid; if ((tok = au_to_subject32(uid, geteuid(), getegid(), uid, gid, pid, pid, &tid)) == NULL) errx(1, "audit error: au_to_subject32() failed"); } au_write(aufd, tok); /* Include the error message. */ if ((tok = au_to_text(errmsg)) == NULL) errx(1, "audit error: au_to_text() failed"); au_write(aufd, tok); if ((tok = au_to_return32(1, errno)) == NULL) errx(1, "audit error: au_to_return32() failed"); au_write(aufd, tok); if (au_close(aufd, 1, AUE_login) == -1) errx(1, "audit error: au_close() was not committed"); } /* * The following tokens are included in the audit record for a logout: * header, subject, return. */ void audit_logout(void) { token_t *tok; int aufd; uid_t uid = pwd->pw_uid; gid_t gid = pwd->pw_gid; pid_t pid = getpid(); int au_cond; /* If we are not auditing, don't cut an audit record; just return. */ if (auditon(A_GETCOND, &au_cond, sizeof(au_cond)) < 0) { if (errno == ENOSYS) return; errx(1, "could not determine audit condition"); } if (au_cond == AUC_NOAUDIT) return; if ((aufd = au_open()) == -1) errx(1, "audit error: au_open() failed"); /* The subject that is created (euid, egid of the current process). */ if ((tok = au_to_subject32(uid, geteuid(), getegid(), uid, gid, pid, pid, &tid)) == NULL) errx(1, "audit error: au_to_subject32() failed"); au_write(aufd, tok); if ((tok = au_to_return32(0, 0)) == NULL) errx(1, "audit error: au_to_return32() failed"); au_write(aufd, tok); if (au_close(aufd, 1, AUE_logout) == -1) errx(1, "audit record was not committed."); } diff --git a/usr.bin/login/login_fbtab.c b/usr.bin/login/login_fbtab.c index 0fb9289346c0..5c846534d0c4 100644 --- a/usr.bin/login/login_fbtab.c +++ b/usr.bin/login/login_fbtab.c @@ -1,140 +1,139 @@ /************************************************************************ * Copyright 1995 by Wietse Venema. All rights reserved. * * This material was originally written and compiled by Wietse Venema at * Eindhoven University of Technology, The Netherlands, in 1990, 1991, * 1992, 1993, 1994 and 1995. * * Redistribution and use in source and binary forms are permitted * provided that this entire copyright notice is duplicated in all such * copies. * * This software is provided "as is" and without any expressed or implied * warranties, including, without limitation, the implied warranties of * merchantibility and fitness for any particular purpose. ************************************************************************/ /* SYNOPSIS void login_fbtab(tty, uid, gid) char *tty; uid_t uid; gid_t gid; DESCRIPTION This module implements device security as described in the SunOS 4.1.x fbtab(5) and SunOS 5.x logindevperm(4) manual pages. The program first looks for /etc/fbtab. If that file cannot be opened it attempts to process /etc/logindevperm. We expect entries with the following format: Comments start with a # and extend to the end of the line. Blank lines or lines with only a comment are ignored. All other lines consist of three fields delimited by whitespace: a login device (/dev/console), an octal permission number (0600), and a ":"-delimited list of devices (/dev/kbd:/dev/mouse). All device names are absolute paths. A path that ends in "*" refers to all directory entries except "." and "..". If the tty argument (relative path) matches a login device name (absolute path), the permissions of the devices in the ":"-delimited list are set as specified in the second field, and their ownership is changed to that of the uid and gid arguments. DIAGNOSTICS Problems are reported via the syslog daemon with severity LOG_ERR. BUGS This module uses strtok(3), which may cause conflicts with other uses of that same routine. AUTHOR Wietse Venema (wietse@wzv.win.tue.nl) Eindhoven University of Technology The Netherlands */ -#include #include #include #include #include #include #include #include #include #include "login.h" #include "pathnames.h" static void login_protect(const char *, char *, int, uid_t, gid_t); #define WSPACE " \t\n" /* login_fbtab - apply protections specified in /etc/fbtab or logindevperm */ void login_fbtab(char *tty, uid_t uid, gid_t gid) { FILE *fp; char buf[BUFSIZ]; char *devname; char *cp; int prot; const char *table; if ((fp = fopen(table = _PATH_FBTAB, "r")) == NULL && (fp = fopen(table = _PATH_LOGINDEVPERM, "r")) == NULL) return; while (fgets(buf, sizeof(buf), fp)) { if ((cp = strchr(buf, '#'))) *cp = 0; /* strip comment */ if ((cp = devname = strtok(buf, WSPACE)) == NULL) continue; /* empty or comment */ if (strncmp(devname, _PATH_DEV, sizeof _PATH_DEV - 1) != 0 || (cp = strtok(NULL, WSPACE)) == NULL || *cp != '0' || sscanf(cp, "%o", &prot) == 0 || prot == 0 || (prot & 0777) != prot || (cp = strtok(NULL, WSPACE)) == NULL) { syslog(LOG_ERR, "%s: bad entry: %s", table, cp ? cp : "(null)"); continue; } if (strcmp(devname + 5, tty) == 0) { for (cp = strtok(cp, ":"); cp; cp = strtok(NULL, ":")) { login_protect(table, cp, prot, uid, gid); } } } fclose(fp); } /* login_protect - protect one device entry */ static void login_protect(const char *table, char *pattern, int mask, uid_t uid, gid_t gid) { glob_t gl; char *path; unsigned int i; if (glob(pattern, GLOB_NOSORT, NULL, &gl) != 0) return; for (i = 0; i < gl.gl_pathc; i++) { path = gl.gl_pathv[i]; /* clear flags of the device */ if (chflags(path, 0) && errno != ENOENT && errno != EOPNOTSUPP) syslog(LOG_ERR, "%s: chflags(%s): %m", table, path); if (chmod(path, mask) && errno != ENOENT) syslog(LOG_ERR, "%s: chmod(%s): %m", table, path); if (chown(path, uid, gid) && errno != ENOENT) syslog(LOG_ERR, "%s: chown(%s): %m", table, path); } globfree(&gl); } diff --git a/usr.bin/lsvfs/lsvfs.c b/usr.bin/lsvfs/lsvfs.c index 4ec983287607..04ed38e8d978 100644 --- a/usr.bin/lsvfs/lsvfs.c +++ b/usr.bin/lsvfs/lsvfs.c @@ -1,100 +1,99 @@ /* * lsvfs - list loaded VFSes * Garrett A. Wollman, September 1994 * This file is in the public domain. * */ -#include #include #include #include #include #include #include #include #define FMT "%-32.32s 0x%08x %5d %s\n" #define HDRFMT "%-32.32s %10s %5.5s %s\n" #define DASHES "-------------------------------- " \ "---------- ----- ---------------\n" static struct flaglist { int flag; const char str[32]; /* must be longer than the longest one. */ } fl[] = { { .flag = VFCF_STATIC, .str = "static", }, { .flag = VFCF_NETWORK, .str = "network", }, { .flag = VFCF_READONLY, .str = "read-only", }, { .flag = VFCF_SYNTHETIC, .str = "synthetic", }, { .flag = VFCF_LOOPBACK, .str = "loopback", }, { .flag = VFCF_UNICODE, .str = "unicode", }, { .flag = VFCF_JAIL, .str = "jail", }, { .flag = VFCF_DELEGADMIN, .str = "delegated-administration", }, }; static const char *fmt_flags(int); int main(int argc, char **argv) { struct xvfsconf vfc, *xvfsp; size_t buflen; int cnt, i, rv = 0; argc--, argv++; printf(HDRFMT, "Filesystem", "Num", "Refs", "Flags"); fputs(DASHES, stdout); if (argc > 0) { for (; argc > 0; argc--, argv++) { if (getvfsbyname(*argv, &vfc) == 0) { printf(FMT, vfc.vfc_name, vfc.vfc_typenum, vfc.vfc_refcount, fmt_flags(vfc.vfc_flags)); } else { warnx("VFS %s unknown or not loaded", *argv); rv++; } } } else { if (sysctlbyname("vfs.conflist", NULL, &buflen, NULL, 0) < 0) err(1, "sysctl(vfs.conflist)"); xvfsp = malloc(buflen); if (xvfsp == NULL) errx(1, "malloc failed"); if (sysctlbyname("vfs.conflist", xvfsp, &buflen, NULL, 0) < 0) err(1, "sysctl(vfs.conflist)"); cnt = buflen / sizeof(struct xvfsconf); for (i = 0; i < cnt; i++) { printf(FMT, xvfsp[i].vfc_name, xvfsp[i].vfc_typenum, xvfsp[i].vfc_refcount, fmt_flags(xvfsp[i].vfc_flags)); } free(xvfsp); } return (rv); } static const char * fmt_flags(int flags) { static char buf[sizeof(struct flaglist) * sizeof(fl)]; int i; buf[0] = '\0'; for (i = 0; i < (int)nitems(fl); i++) { if ((flags & fl[i].flag) != 0) { strlcat(buf, fl[i].str, sizeof(buf)); strlcat(buf, ", ", sizeof(buf)); } } /* Zap the trailing comma + space. */ if (buf[0] != '\0') buf[strlen(buf) - 2] = '\0'; return (buf); } diff --git a/usr.bin/m4/misc.c b/usr.bin/m4/misc.c index 4533e106a37f..d641ec56afeb 100644 --- a/usr.bin/m4/misc.c +++ b/usr.bin/m4/misc.c @@ -1,470 +1,469 @@ /* $OpenBSD: misc.c,v 1.47 2017/06/15 13:48:42 bcallah Exp $ */ /* $NetBSD: misc.c,v 1.6 1995/09/28 05:37:41 tls Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Ozan Yigit at York University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include "mdef.h" #include "stdd.h" #include "extern.h" #include "pathnames.h" char *ep; /* first free char in strspace */ static char *strspace; /* string space for evaluation */ char *endest; /* end of string space */ static size_t strsize = STRSPMAX; static size_t bufsize = BUFSIZE; unsigned char *buf; /* push-back buffer */ unsigned char *bufbase; /* the base for current ilevel */ unsigned char *bbase[MAXINP]; /* the base for each ilevel */ unsigned char *bp; /* first available character */ unsigned char *endpbb; /* end of push-back buffer */ /* * find the index of second str in the first str. */ ptrdiff_t indx(const char *s1, const char *s2) { char *t; t = strstr(s1, s2); if (t == NULL) return (-1); else return (t - s1); } /* * pushback - push character back onto input */ void pushback(int c) { if (c == EOF) return; if (bp >= endpbb) enlarge_bufspace(); *bp++ = c; } /* * pbstr - push string back onto input * pushback is replicated to improve * performance. */ void pbstr(const char *s) { size_t n; n = strlen(s); while (endpbb - bp <= (long)n) enlarge_bufspace(); while (n > 0) *bp++ = s[--n]; } /* * pbnum - convert number to string, push back on input. */ void pbnum(int n) { pbnumbase(n, 10, 0); } void pbnumbase(int n, int base, int d) { static char digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz"; int num; int printed = 0; if (base > 36) m4errx(1, "base %d > 36: not supported.", base); if (base < 2) m4errx(1, "bad base %d for conversion.", base); num = (n < 0) ? -n : n; do { pushback(digits[num % base]); printed++; } while ((num /= base) > 0); if (n < 0) printed++; while (printed++ < d) pushback('0'); if (n < 0) pushback('-'); } /* * pbunsigned - convert unsigned long to string, push back on input. */ void pbunsigned(unsigned long n) { do { pushback(n % 10 + '0'); } while ((n /= 10) > 0); } void initspaces(void) { int i; strspace = xalloc(strsize+1, NULL); ep = strspace; endest = strspace+strsize; buf = xalloc(bufsize, NULL); bufbase = buf; bp = buf; endpbb = buf + bufsize; for (i = 0; i < MAXINP; i++) bbase[i] = buf; } void enlarge_strspace(void) { char *newstrspace; int i; strsize *= 2; newstrspace = malloc(strsize + 1); if (!newstrspace) errx(1, "string space overflow"); memcpy(newstrspace, strspace, strsize/2); for (i = 0; i <= sp; i++) if (sstack[i] == STORAGE_STRSPACE) mstack[i].sstr = (mstack[i].sstr - strspace) + newstrspace; ep = (ep-strspace) + newstrspace; free(strspace); strspace = newstrspace; endest = strspace + strsize; } void enlarge_bufspace(void) { unsigned char *newbuf; int i; bufsize += bufsize/2; newbuf = xrealloc(buf, bufsize, "too many characters pushed back"); for (i = 0; i < MAXINP; i++) bbase[i] = (bbase[i]-buf)+newbuf; bp = (bp-buf)+newbuf; bufbase = (bufbase-buf)+newbuf; buf = newbuf; endpbb = buf+bufsize; } /* * chrsave - put single char on string space */ void chrsave(int c) { if (ep >= endest) enlarge_strspace(); *ep++ = c; } /* * read in a diversion file, and dispose it. */ void getdiv(int n) { int c; if (active == outfile[n]) m4errx(1, "undivert: diversion still active."); rewind(outfile[n]); while ((c = getc(outfile[n])) != EOF) putc(c, active); (void) fclose(outfile[n]); outfile[n] = NULL; } void onintr(int signo __unused) { #define intrmessage "m4: interrupted.\n" write(STDERR_FILENO, intrmessage, sizeof(intrmessage)-1); _exit(1); } /* * killdiv - get rid of the diversion files */ void killdiv(void) { int n; for (n = 0; n < maxout; n++) if (outfile[n] != NULL) { (void) fclose(outfile[n]); } } extern char *__progname; void m4errx(int eval, const char *fmt, ...) { fprintf(stderr, "%s: ", __progname); fprintf(stderr, "%s at line %lu: ", CURRENT_NAME, CURRENT_LINE); if (fmt != NULL) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } fprintf(stderr, "\n"); exit(eval); } /* * resizedivs: allocate more diversion files */ void resizedivs(int n) { int i; outfile = xreallocarray(outfile, n, sizeof(FILE *), "too many diverts %d", n); for (i = maxout; i < n; i++) outfile[i] = NULL; maxout = n; } void * xalloc(size_t n, const char *fmt, ...) { void *p = malloc(n); if (p == NULL) { if (fmt == NULL) err(1, "malloc"); else { va_list va; va_start(va, fmt); verr(1, fmt, va); va_end(va); } } return p; } void * xcalloc(size_t n, size_t s, const char *fmt, ...) { void *p = calloc(n, s); if (p == NULL) { if (fmt == NULL) err(1, "calloc"); else { va_list va; va_start(va, fmt); verr(1, fmt, va); va_end(va); } } return p; } void * xrealloc(void *old, size_t n, const char *fmt, ...) { char *p = realloc(old, n); if (p == NULL) { free(old); if (fmt == NULL) err(1, "realloc"); else { va_list va; va_start(va, fmt); verr(1, fmt, va); va_end(va); } } return p; } void * xreallocarray(void *old, size_t s1, size_t s2, const char *fmt, ...) { void *p = reallocarray(old, s1, s2); if (p == NULL) { free(old); if (fmt == NULL) err(1, "reallocarray"); else { va_list va; va_start(va, fmt); verr(1, fmt, va); va_end(va); } } return p; } char * xstrdup(const char *s) { char *p = strdup(s); if (p == NULL) err(1, "strdup"); return p; } void usage(void) { fprintf(stderr, "usage: m4 [-EgPs] [-Dname[=value]] [-d flags] " "[-I dirname] [-o filename]\n" "\t[-t macro] [-Uname] [file ...]\n"); exit(1); } int obtain_char(struct input_file *f) { if (f->c == EOF) return EOF; f->c = fgetc(f->file); if (f->c == '\n') f->lineno++; return f->c; } void set_input(struct input_file *f, FILE *real, const char *name) { f->file = real; f->lineno = 1; f->c = 0; f->name = xstrdup(name); emit_synchline(); } void do_emit_synchline(void) { fprintf(active, "#line %lu \"%s\"\n", infile[ilevel].lineno, infile[ilevel].name); infile[ilevel].synch_lineno = infile[ilevel].lineno; } void release_input(struct input_file *f) { if (ferror(f->file)) errx(1, "Fatal error reading from %s\n", f->name); if (f->file != stdin) fclose(f->file); f->c = EOF; /* * XXX can't free filename, as there might still be * error information pointing to it. */ } void doprintlineno(struct input_file *f) { pbunsigned(f->lineno); } void doprintfilename(struct input_file *f) { pbstr(rquote); pbstr(f->name); pbstr(lquote); } /* * buffer_mark/dump_buffer: allows one to save a mark in a buffer, * and later dump everything that was added since then to a file. */ size_t buffer_mark(void) { return bp - buf; } void dump_buffer(FILE *f, size_t m) { unsigned char *s; for (s = bp; s-buf > (long)m;) fputc(*--s, f); } diff --git a/usr.bin/mail/cmd1.c b/usr.bin/mail/cmd1.c index 28e695f6a7f1..8ea361efab93 100644 --- a/usr.bin/mail/cmd1.c +++ b/usr.bin/mail/cmd1.c @@ -1,472 +1,469 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include "extern.h" /* * Mail -- a mail program * * User commands. */ /* * Print the current active headings. * Don't change dot if invoker didn't give an argument. */ static int screen; int headers(void *v) { int *msgvec = v; int n, mesg, flag, size; struct message *mp; size = screensize(); n = msgvec[0]; if (n != 0) screen = (n-1)/size; if (screen < 0) screen = 0; mp = &message[screen * size]; if (mp >= &message[msgCount]) mp = &message[msgCount - size]; if (mp < &message[0]) mp = &message[0]; flag = 0; mesg = mp - &message[0]; if (dot != &message[n-1]) dot = mp; for (; mp < &message[msgCount]; mp++) { mesg++; if (mp->m_flag & MDELETED) continue; if (flag++ >= size) break; printhead(mesg); } if (flag == 0) { printf("No more mail.\n"); return (1); } return (0); } /* * Scroll to the next/previous screen */ int scroll(void *v) { char *arg = v; int s, size; int cur[1]; cur[0] = 0; size = screensize(); s = screen; switch (*arg) { case 0: case '+': s++; if (s * size >= msgCount) { printf("On last screenful of messages\n"); return (0); } screen = s; break; case '-': if (--s < 0) { printf("On first screenful of messages\n"); return (0); } screen = s; break; default: printf("Unrecognized scrolling command \"%s\"\n", arg); return (1); } return (headers(cur)); } /* * Compute screen size. */ int screensize(void) { int s; char *cp; if ((cp = value("screen")) != NULL && (s = atoi(cp)) > 0) return (s); return (screenheight - 4); } /* * Print out the headlines for each message * in the passed message list. */ int from(void *v) { int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) printhead(*ip); if (--ip >= msgvec) dot = &message[*ip - 1]; return (0); } /* * Print out the header of a specific message. * This is a slight improvement to the standard one. */ void printhead(int mesg) { struct message *mp; char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind; char pbuf[BUFSIZ]; struct headline hl; int subjlen; char *name; mp = &message[mesg-1]; (void)readline(setinput(mp), headline, LINESIZE); if ((subjline = hfield("subject", mp)) == NULL) subjline = hfield("subj", mp); /* * Bletch! */ curind = dot == mp ? '>' : ' '; dispc = ' '; if (mp->m_flag & MSAVED) dispc = '*'; if (mp->m_flag & MPRESERVE) dispc = 'P'; if ((mp->m_flag & (MREAD|MNEW)) == MNEW) dispc = 'N'; if ((mp->m_flag & (MREAD|MNEW)) == 0) dispc = 'U'; if (mp->m_flag & MBOX) dispc = 'M'; parse(headline, &hl, pbuf); sprintf(wcount, "%3ld/%-5ld", mp->m_lines, mp->m_size); subjlen = screenwidth - 50 - strlen(wcount); name = value("show-rcpt") != NULL ? skin(hfield("to", mp)) : nameof(mp, 0); if (subjline == NULL || subjlen < 0) /* pretty pathetic */ printf("%c%c%3d %-20.20s %16.16s %s\n", curind, dispc, mesg, name, hl.l_date, wcount); else printf("%c%c%3d %-20.20s %16.16s %s \"%.*s\"\n", curind, dispc, mesg, name, hl.l_date, wcount, subjlen, subjline); } /* * Print out the value of dot. */ int pdot(void *arg __unused) { printf("%td\n", dot - &message[0] + 1); return (0); } /* * Print out all the possible commands. */ int pcmdlist(void *arg __unused) { extern const struct cmd cmdtab[]; const struct cmd *cp; int cc; printf("Commands are:\n"); for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) { cc += strlen(cp->c_name) + 2; if (cc > 72) { printf("\n"); cc = strlen(cp->c_name) + 2; } if ((cp+1)->c_name != NULL) printf("%s, ", cp->c_name); else printf("%s\n", cp->c_name); } return (0); } /* * Paginate messages, honor ignored fields. */ int more(void *v) { int *msgvec = v; return (type1(msgvec, 1, 1)); } /* * Paginate messages, even printing ignored fields. */ int More(void *v) { int *msgvec = v; return (type1(msgvec, 0, 1)); } /* * Type out messages, honor ignored fields. */ int type(void *v) { int *msgvec = v; return (type1(msgvec, 1, 0)); } /* * Type out messages, even printing ignored fields. */ int Type(void *v) { int *msgvec = v; return (type1(msgvec, 0, 0)); } /* * Type out the messages requested. */ static jmp_buf pipestop; int type1(int *msgvec, int doign, int page) { int nlines, *ip; struct message *mp; char *cp; FILE *obuf; obuf = stdout; if (setjmp(pipestop)) goto close_pipe; if (value("interactive") != NULL && (page || (cp = value("crt")) != NULL)) { nlines = 0; if (!page) { for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) nlines += message[*ip - 1].m_lines; } if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) { cp = value("PAGER"); if (cp == NULL || *cp == '\0') cp = _PATH_LESS; obuf = Popen(cp, "w"); if (obuf == NULL) { warnx("%s", cp); obuf = stdout; } else (void)signal(SIGPIPE, brokpipe); } } /* * Send messages to the output. * */ for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) { mp = &message[*ip - 1]; touch(mp); dot = mp; if (value("quiet") == NULL) fprintf(obuf, "Message %d:\n", *ip); (void)sendmessage(mp, obuf, doign ? ignore : 0, NULL); } close_pipe: if (obuf != stdout) { /* * Ignore SIGPIPE so it can't cause a duplicate close. */ (void)signal(SIGPIPE, SIG_IGN); (void)Pclose(obuf); (void)signal(SIGPIPE, SIG_DFL); } return (0); } /* * Respond to a broken pipe signal -- * probably caused by quitting more. */ /*ARGSUSED*/ void brokpipe(int signo __unused) { longjmp(pipestop, 1); } /* * Print the top so many lines of each desired message. * The number of lines is taken from the variable "toplines" * and defaults to 5. */ int top(void *v) { int *msgvec = v; int *ip; struct message *mp; int c, topl, lines, lineb; char *valtop, linebuf[LINESIZE]; FILE *ibuf; topl = 5; valtop = value("toplines"); if (valtop != NULL) { topl = atoi(valtop); if (topl < 0 || topl > 10000) topl = 5; } lineb = 1; for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mp = &message[*ip - 1]; touch(mp); dot = mp; if (value("quiet") == NULL) printf("Message %d:\n", *ip); ibuf = setinput(mp); c = mp->m_lines; if (!lineb) printf("\n"); for (lines = 0; lines < c && lines <= topl; lines++) { if (readline(ibuf, linebuf, sizeof(linebuf)) < 0) break; puts(linebuf); lineb = strspn(linebuf, " \t") == strlen(linebuf); } } return (0); } /* * Touch all the given messages so that they will * get mboxed. */ int stouch(void *v) { int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { dot = &message[*ip-1]; dot->m_flag |= MTOUCH; dot->m_flag &= ~MPRESERVE; } return (0); } /* * Make sure all passed messages get mboxed. */ int mboxit(void *v) { int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { dot = &message[*ip-1]; dot->m_flag |= MTOUCH|MBOX; dot->m_flag &= ~MPRESERVE; } return (0); } /* * List the folders the user currently has. */ int folders(void *arg __unused) { char dirname[PATHSIZE]; char *cmd; if (getfold(dirname, sizeof(dirname)) < 0) { printf("No value set for \"folder\"\n"); return (1); } if ((cmd = value("LISTER")) == NULL) cmd = "ls"; (void)run_command(cmd, 0, -1, -1, dirname, NULL); return (0); } /* * Update the mail file with any new messages that have * come in since we started reading mail. */ int inc(void *v __unused) { int nmsg, mdot; nmsg = incfile(); if (nmsg == 0) printf("No new mail.\n"); else if (nmsg > 0) { mdot = newfileinfo(msgCount - nmsg); dot = &message[mdot - 1]; } else printf("\"inc\" command failed...\n"); return (0); } diff --git a/usr.bin/mail/cmd2.c b/usr.bin/mail/cmd2.c index 27901a2078c6..5c670b7867e7 100644 --- a/usr.bin/mail/cmd2.c +++ b/usr.bin/mail/cmd2.c @@ -1,513 +1,510 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include #include "extern.h" /* * Mail -- a mail program * * More user commands. */ extern int wait_status; /* * If any arguments were given, go to the next applicable argument * following dot, otherwise, go to the next applicable message. * If given as first command with no arguments, print first message. */ int next(void *v) { struct message *mp; int *msgvec = v; int *ip, *ip2, list[2], mdot; if (*msgvec != 0) { /* * If some messages were supplied, find the * first applicable one following dot using * wrap around. */ mdot = dot - &message[0] + 1; /* * Find the first message in the supplied * message list which follows dot. */ for (ip = msgvec; *ip != 0; ip++) if (*ip > mdot) break; if (*ip == 0) ip = msgvec; ip2 = ip; do { mp = &message[*ip2 - 1]; if ((mp->m_flag & MDELETED) == 0) { dot = mp; goto hitit; } if (*ip2 != 0) ip2++; if (*ip2 == 0) ip2 = msgvec; } while (ip2 != ip); printf("No messages applicable\n"); return (1); } /* * If this is the first command, select message 1. * Note that this must exist for us to get here at all. */ if (!sawcom) goto hitit; /* * Just find the next good message after dot, no * wraparound. */ for (mp = dot+1; mp < &message[msgCount]; mp++) if ((mp->m_flag & (MDELETED|MSAVED)) == 0) break; if (mp >= &message[msgCount]) { printf("At EOF\n"); return (0); } dot = mp; hitit: /* * Print dot. */ list[0] = dot - &message[0] + 1; list[1] = 0; return (type(list)); } /* * Save a message in a file. Mark the message as saved * so we can discard when the user quits. */ int save(void *v) { char *str = v; return (save1(str, 1, "save", saveignore)); } /* * Copy a message to a file without affected its saved-ness */ int copycmd(void *v) { char *str = v; return (save1(str, 0, "copy", saveignore)); } /* * Save/copy the indicated messages at the end of the passed file name. * If mark is true, mark the message "saved." */ int save1(char str[], int mark, const char *cmd, struct ignoretab *ignore) { struct message *mp; char *file; const char *disp; int f, *msgvec, *ip; FILE *obuf; msgvec = (int *)salloc((msgCount + 2) * sizeof(*msgvec)); if ((file = snarf(str, &f)) == NULL) return (1); if (!f) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { printf("No messages to %s.\n", cmd); return (1); } msgvec[1] = 0; } if (f && getmsglist(str, msgvec, 0) < 0) return (1); if ((file = expand(file)) == NULL) return (1); printf("\"%s\" ", file); (void)fflush(stdout); if (access(file, 0) >= 0) disp = "[Appended]"; else disp = "[New file]"; if ((obuf = Fopen(file, "a")) == NULL) { warn((char *)NULL); return (1); } for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mp = &message[*ip - 1]; touch(mp); if (sendmessage(mp, obuf, ignore, NULL) < 0) { warnx("%s", file); (void)Fclose(obuf); return (1); } if (mark) mp->m_flag |= MSAVED; } (void)fflush(obuf); if (ferror(obuf)) warn("%s", file); (void)Fclose(obuf); printf("%s\n", disp); return (0); } /* * Write the indicated messages at the end of the passed * file name, minus header and trailing blank line. */ int swrite(void *v) { char *str = v; return (save1(str, 1, "write", ignoreall)); } /* * Snarf the file from the end of the command line and * return a pointer to it. If there is no file attached, * just return NULL. Put a null in front of the file * name so that the message list processing won't see it, * unless the file name is the only thing on the line, in * which case, return 0 in the reference flag variable. */ char * snarf(char *linebuf, int *flag) { char *cp; *flag = 1; cp = strlen(linebuf) + linebuf - 1; /* * Strip away trailing blanks. */ while (cp > linebuf && isspace((unsigned char)*cp)) cp--; *++cp = '\0'; /* * Now search for the beginning of the file name. */ while (cp > linebuf && !isspace((unsigned char)*cp)) cp--; if (*cp == '\0') { printf("No file specified.\n"); return (NULL); } if (isspace((unsigned char)*cp)) *cp++ = '\0'; else *flag = 0; return (cp); } /* * Delete messages. */ int deletecmd(void *v) { int *msgvec = v; delm(msgvec); return (0); } /* * Delete messages, then type the new dot. */ int deltype(void *v) { int *msgvec = v; int list[2]; int lastdot; lastdot = dot - &message[0] + 1; if (delm(msgvec) >= 0) { list[0] = dot - &message[0] + 1; if (list[0] > lastdot) { touch(dot); list[1] = 0; return (type(list)); } printf("At EOF\n"); } else printf("No more messages\n"); return (0); } /* * Delete the indicated messages. * Set dot to some nice place afterwards. * Internal interface. */ int delm(int *msgvec) { struct message *mp; int *ip, last; last = 0; for (ip = msgvec; *ip != 0; ip++) { mp = &message[*ip - 1]; touch(mp); mp->m_flag |= MDELETED|MTOUCH; mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); last = *ip; } if (last != 0) { dot = &message[last-1]; last = first(0, MDELETED); if (last != 0) { dot = &message[last-1]; return (0); } else { dot = &message[0]; return (-1); } } /* * Following can't happen -- it keeps lint happy */ return (-1); } /* * Undelete the indicated messages. */ int undeletecmd(void *v) { int *msgvec = v; int *ip; struct message *mp; for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { mp = &message[*ip - 1]; touch(mp); dot = mp; mp->m_flag &= ~MDELETED; } return (0); } /* * Interactively dump core on "core" */ int core(void *arg __unused) { int pid; switch (pid = fork()) { case -1: warn("fork"); return (1); case 0: abort(); _exit(1); } printf("Okie dokie"); (void)fflush(stdout); wait_child(pid); if (WIFSIGNALED(wait_status) && WCOREDUMP(wait_status)) printf(" -- Core dumped.\n"); else printf(" -- Can't dump core.\n"); return (0); } /* * Clobber as many bytes of stack as the user requests. */ int clobber(void *arg) { char **argv = arg; int times; if (argv[0] == 0) times = 1; else times = (atoi(argv[0]) + 511) / 512; clob1(times); return (0); } /* * Clobber the stack. */ void clob1(int n) { char buf[512]; char *cp; if (n <= 0) return; for (cp = buf; cp < &buf[512]; *cp++ = 0xFF) ; clob1(n - 1); } /* * Add the given header fields to the retained list. * If no arguments, print the current list of retained fields. */ int retfield(void *v) { char **list = v; return (ignore1(list, ignore + 1, "retained")); } /* * Add the given header fields to the ignored list. * If no arguments, print the current list of ignored fields. */ int igfield(void *v) { char **list = v; return (ignore1(list, ignore, "ignored")); } int saveretfield(void *v) { char **list = v; return (ignore1(list, saveignore + 1, "retained")); } int saveigfield(void *v) { char **list = v; return (ignore1(list, saveignore, "ignored")); } int ignore1(char **list, struct ignoretab *tab, const char *which) { char field[LINESIZE]; char **ap; struct ignore *igp; int h; if (*list == NULL) return (igshow(tab, which)); for (ap = list; *ap != 0; ap++) { istrncpy(field, *ap, sizeof(field)); if (member(field, tab)) continue; h = hash(field); igp = calloc(1, sizeof(struct ignore)); igp->i_field = calloc((unsigned)strlen(field) + 1, sizeof(char)); strcpy(igp->i_field, field); igp->i_link = tab->i_head[h]; tab->i_head[h] = igp; tab->i_count++; } return (0); } /* * Print out all currently retained fields. */ int igshow(struct ignoretab *tab, const char *which) { int h; struct ignore *igp; char **ap, **ring; if (tab->i_count == 0) { printf("No fields currently being %s.\n", which); return (0); } ring = (char **)salloc((tab->i_count + 1) * sizeof(char *)); ap = ring; for (h = 0; h < HSHSIZE; h++) for (igp = tab->i_head[h]; igp != NULL; igp = igp->i_link) *ap++ = igp->i_field; *ap = 0; qsort(ring, tab->i_count, sizeof(char *), igcomp); for (ap = ring; *ap != 0; ap++) printf("%s\n", *ap); return (0); } /* * Compare two names for sorting ignored field list. */ int igcomp(const void *l, const void *r) { return (strcmp(*(const char **)l, *(const char **)r)); } diff --git a/usr.bin/mail/cmd3.c b/usr.bin/mail/cmd3.c index bf1962ead2a7..fbc71ca87db4 100644 --- a/usr.bin/mail/cmd3.c +++ b/usr.bin/mail/cmd3.c @@ -1,726 +1,723 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include "extern.h" /* * Mail -- a mail program * * Still more user commands. */ /* * Process a shell escape by saving signals, ignoring signals, * and forking a sh -c */ int shell(void *str) { sig_t sigint = signal(SIGINT, SIG_IGN); char *sh; char cmd[BUFSIZ]; if (strlcpy(cmd, str, sizeof(cmd)) >= sizeof(cmd)) return (1); if (bangexp(cmd, sizeof(cmd)) < 0) return (1); if ((sh = value("SHELL")) == NULL) sh = _PATH_CSHELL; (void)run_command(sh, 0, -1, -1, "-c", cmd, NULL); (void)signal(SIGINT, sigint); printf("!\n"); return (0); } /* * Fork an interactive shell. */ /*ARGSUSED*/ int dosh(void *str __unused) { sig_t sigint = signal(SIGINT, SIG_IGN); char *sh; if ((sh = value("SHELL")) == NULL) sh = _PATH_CSHELL; (void)run_command(sh, 0, -1, -1, NULL); (void)signal(SIGINT, sigint); printf("\n"); return (0); } /* * Expand the shell escape by expanding unescaped !'s into the * last issued command where possible. */ int bangexp(char *str, size_t strsize) { char bangbuf[BUFSIZ]; static char lastbang[BUFSIZ]; char *cp, *cp2; int n, changed = 0; cp = str; cp2 = bangbuf; n = sizeof(bangbuf); while (*cp != '\0') { if (*cp == '!') { if (n < (int)strlen(lastbang)) { overf: printf("Command buffer overflow\n"); return (-1); } changed++; if (strlcpy(cp2, lastbang, sizeof(bangbuf) - (cp2 - bangbuf)) >= sizeof(bangbuf) - (cp2 - bangbuf)) goto overf; cp2 += strlen(lastbang); n -= strlen(lastbang); cp++; continue; } if (*cp == '\\' && cp[1] == '!') { if (--n <= 1) goto overf; *cp2++ = '!'; cp += 2; changed++; } if (--n <= 1) goto overf; *cp2++ = *cp++; } *cp2 = 0; if (changed) { printf("!%s\n", bangbuf); (void)fflush(stdout); } if (strlcpy(str, bangbuf, strsize) >= strsize) goto overf; if (strlcpy(lastbang, bangbuf, sizeof(lastbang)) >= sizeof(lastbang)) goto overf; return (0); } /* * Print out a nice help message from some file or another. */ int help(void *arg __unused) { int c; FILE *f; if ((f = Fopen(_PATH_HELP, "r")) == NULL) { warn("%s", _PATH_HELP); return (1); } while ((c = getc(f)) != EOF) putchar(c); (void)Fclose(f); return (0); } /* * Change user's working directory. */ int schdir(void *v) { char **arglist = v; char *cp; if (*arglist == NULL) { if (homedir == NULL) return (1); cp = homedir; } else if ((cp = expand(*arglist)) == NULL) return (1); if (chdir(cp) < 0) { warn("%s", cp); return (1); } return (0); } int respond(void *v) { int *msgvec = v; if (value("Replyall") == NULL && value("flipr") == NULL) return (dorespond(msgvec)); else return (doRespond(msgvec)); } /* * Reply to a list of messages. Extract each name from the * message header and send them off to mail1() */ int dorespond(int *msgvec) { struct message *mp; char *cp, *rcv, *replyto; char **ap; struct name *np; struct header head; if (msgvec[1] != 0) { printf("Sorry, can't reply to multiple messages at once\n"); return (1); } mp = &message[msgvec[0] - 1]; touch(mp); dot = mp; if ((rcv = skin(hfield("from", mp))) == NULL) rcv = skin(nameof(mp, 1)); if ((replyto = skin(hfield("reply-to", mp))) != NULL) np = extract(replyto, GTO); else if ((cp = skin(hfield("to", mp))) != NULL) np = extract(cp, GTO); else np = NULL; np = elide(np); /* * Delete my name from the reply list, * and with it, all my alternate names. */ np = delname(np, myname); if (altnames) for (ap = altnames; *ap != NULL; ap++) np = delname(np, *ap); if (np != NULL && replyto == NULL) np = cat(np, extract(rcv, GTO)); else if (np == NULL) { if (replyto != NULL) printf("Empty reply-to field -- replying to author\n"); np = extract(rcv, GTO); } head.h_to = np; if ((head.h_subject = hfield("subject", mp)) == NULL) head.h_subject = hfield("subj", mp); head.h_subject = reedit(head.h_subject); if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) { np = elide(extract(cp, GCC)); np = delname(np, myname); if (altnames != 0) for (ap = altnames; *ap != NULL; ap++) np = delname(np, *ap); head.h_cc = np; } else head.h_cc = NULL; head.h_bcc = NULL; head.h_smopts = NULL; head.h_replyto = value("REPLYTO"); head.h_inreplyto = skin(hfield("message-id", mp)); mail1(&head, 1); return (0); } /* * Modify the message subject to begin with "Re:" if * it does not already. */ char * reedit(char *subj) { char *newsubj; if (subj == NULL) return (NULL); if ((subj[0] == 'r' || subj[0] == 'R') && (subj[1] == 'e' || subj[1] == 'E') && subj[2] == ':') return (subj); newsubj = salloc(strlen(subj) + 5); sprintf(newsubj, "Re: %s", subj); return (newsubj); } /* * Preserve the named messages, so that they will be sent * back to the system mailbox. */ int preserve(void *v) { int *msgvec = v; int *ip, mesg; struct message *mp; if (edit) { printf("Cannot \"preserve\" in edit mode\n"); return (1); } for (ip = msgvec; *ip != 0; ip++) { mesg = *ip; mp = &message[mesg-1]; mp->m_flag |= MPRESERVE; mp->m_flag &= ~MBOX; dot = mp; } return (0); } /* * Mark all given messages as unread. */ int unread(void *v) { int *msgvec = v; int *ip; for (ip = msgvec; *ip != 0; ip++) { dot = &message[*ip-1]; dot->m_flag &= ~(MREAD|MTOUCH); dot->m_flag |= MSTATUS; } return (0); } /* * Print the size of each message. */ int messize(void *v) { int *msgvec = v; struct message *mp; int *ip, mesg; for (ip = msgvec; *ip != 0; ip++) { mesg = *ip; mp = &message[mesg-1]; printf("%d: %ld/%ld\n", mesg, mp->m_lines, mp->m_size); } return (0); } /* * Quit quickly. If we are sourcing, just pop the input level * by returning an error. */ int rexit(void *v) { if (sourcing) return (1); exit(0); /*NOTREACHED*/ } /* * Set or display a variable value. Syntax is similar to that * of csh. */ int set(void *v) { char **arglist = v; struct var *vp; char *cp, *cp2; char varbuf[BUFSIZ], **ap, **p; int errs, h, s; if (*arglist == NULL) { for (h = 0, s = 1; h < HSHSIZE; h++) for (vp = variables[h]; vp != NULL; vp = vp->v_link) s++; ap = (char **)salloc(s * sizeof(*ap)); for (h = 0, p = ap; h < HSHSIZE; h++) for (vp = variables[h]; vp != NULL; vp = vp->v_link) *p++ = vp->v_name; *p = NULL; sort(ap); for (p = ap; *p != NULL; p++) printf("%s\t%s\n", *p, value(*p)); return (0); } errs = 0; for (ap = arglist; *ap != NULL; ap++) { cp = *ap; cp2 = varbuf; while (cp2 < varbuf + sizeof(varbuf) - 1 && *cp != '=' && *cp != '\0') *cp2++ = *cp++; *cp2 = '\0'; if (*cp == '\0') cp = ""; else cp++; if (equal(varbuf, "")) { printf("Non-null variable name required\n"); errs++; continue; } assign(varbuf, cp); } return (errs); } /* * Unset a bunch of variable values. */ int unset(void *v) { char **arglist = v; struct var *vp, *vp2; int errs, h; char **ap; errs = 0; for (ap = arglist; *ap != NULL; ap++) { if ((vp2 = lookup(*ap)) == NULL) { if (getenv(*ap)) unsetenv(*ap); else if (!sourcing) { printf("\"%s\": undefined variable\n", *ap); errs++; } continue; } h = hash(*ap); if (vp2 == variables[h]) { variables[h] = variables[h]->v_link; vfree(vp2->v_name); vfree(vp2->v_value); (void)free(vp2); continue; } for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link) ; vp->v_link = vp2->v_link; vfree(vp2->v_name); vfree(vp2->v_value); (void)free(vp2); } return (errs); } /* * Put add users to a group. */ int group(void *v) { char **argv = v; struct grouphead *gh; struct group *gp; char **ap, *gname, **p; int h, s; if (*argv == NULL) { for (h = 0, s = 1; h < HSHSIZE; h++) for (gh = groups[h]; gh != NULL; gh = gh->g_link) s++; ap = (char **)salloc(s * sizeof(*ap)); for (h = 0, p = ap; h < HSHSIZE; h++) for (gh = groups[h]; gh != NULL; gh = gh->g_link) *p++ = gh->g_name; *p = NULL; sort(ap); for (p = ap; *p != NULL; p++) printgroup(*p); return (0); } if (argv[1] == NULL) { printgroup(*argv); return (0); } gname = *argv; h = hash(gname); if ((gh = findgroup(gname)) == NULL) { if ((gh = calloc(1, sizeof(*gh))) == NULL) err(1, "Out of memory"); gh->g_name = vcopy(gname); gh->g_list = NULL; gh->g_link = groups[h]; groups[h] = gh; } /* * Insert names from the command list into the group. * Who cares if there are duplicates? They get tossed * later anyway. */ for (ap = argv+1; *ap != NULL; ap++) { if ((gp = calloc(1, sizeof(*gp))) == NULL) err(1, "Out of memory"); gp->ge_name = vcopy(*ap); gp->ge_link = gh->g_list; gh->g_list = gp; } return (0); } /* * Sort the passed string vecotor into ascending dictionary * order. */ void sort(char **list) { char **ap; for (ap = list; *ap != NULL; ap++) ; if (ap-list < 2) return; qsort(list, ap-list, sizeof(*list), diction); } /* * Do a dictionary order comparison of the arguments from * qsort. */ int diction(const void *a, const void *b) { return (strcmp(*(const char **)a, *(const char **)b)); } /* * The do nothing command for comments. */ /*ARGSUSED*/ int null(void *arg __unused) { return (0); } /* * Change to another file. With no argument, print information about * the current file. */ int file(void *arg) { char **argv = arg; if (argv[0] == NULL) { newfileinfo(0); return (0); } if (setfile(*argv) < 0) return (1); announce(); return (0); } /* * Expand file names like echo */ int echo(void *arg) { char **argv = arg; char **ap, *cp; for (ap = argv; *ap != NULL; ap++) { cp = *ap; if ((cp = expand(cp)) != NULL) { if (ap != argv) printf(" "); printf("%s", cp); } } printf("\n"); return (0); } int Respond(void *msgvec) { if (value("Replyall") == NULL && value("flipr") == NULL) return (doRespond(msgvec)); else return (dorespond(msgvec)); } /* * Reply to a series of messages by simply mailing to the senders * and not messing around with the To: and Cc: lists as in normal * reply. */ int doRespond(int msgvec[]) { struct header head; struct message *mp; int *ap; char *cp, *mid; head.h_to = NULL; for (ap = msgvec; *ap != 0; ap++) { mp = &message[*ap - 1]; touch(mp); dot = mp; if ((cp = skin(hfield("from", mp))) == NULL) cp = skin(nameof(mp, 2)); head.h_to = cat(head.h_to, extract(cp, GTO)); mid = skin(hfield("message-id", mp)); } if (head.h_to == NULL) return (0); mp = &message[msgvec[0] - 1]; if ((head.h_subject = hfield("subject", mp)) == NULL) head.h_subject = hfield("subj", mp); head.h_subject = reedit(head.h_subject); head.h_cc = NULL; head.h_bcc = NULL; head.h_smopts = NULL; head.h_replyto = value("REPLYTO"); head.h_inreplyto = mid; mail1(&head, 1); return (0); } /* * Conditional commands. These allow one to parameterize one's * .mailrc and do some things if sending, others if receiving. */ int ifcmd(void *arg) { char **argv = arg; char *cp; if (cond != CANY) { printf("Illegal nested \"if\"\n"); return (1); } cond = CANY; cp = argv[0]; switch (*cp) { case 'r': case 'R': cond = CRCV; break; case 's': case 'S': cond = CSEND; break; default: printf("Unrecognized if-keyword: \"%s\"\n", cp); return (1); } return (0); } /* * Implement 'else'. This is pretty simple -- we just * flip over the conditional flag. */ int elsecmd(void *arg __unused) { switch (cond) { case CANY: printf("\"Else\" without matching \"if\"\n"); return (1); case CSEND: cond = CRCV; break; case CRCV: cond = CSEND; break; default: printf("Mail's idea of conditions is screwed up\n"); cond = CANY; break; } return (0); } /* * End of if statement. Just set cond back to anything. */ int endifcmd(void *arg __unused) { if (cond == CANY) { printf("\"Endif\" without matching \"if\"\n"); return (1); } cond = CANY; return (0); } /* * Set the list of alternate names. */ int alternates(void *arg) { char **namelist = arg; int c; char **ap, **ap2, *cp; c = argcount(namelist) + 1; if (c == 1) { if (altnames == 0) return (0); for (ap = altnames; *ap != NULL; ap++) printf("%s ", *ap); printf("\n"); return (0); } if (altnames != 0) (void)free(altnames); if ((altnames = calloc((unsigned)c, sizeof(char *))) == NULL) err(1, "Out of memory"); for (ap = namelist, ap2 = altnames; *ap != NULL; ap++, ap2++) { cp = calloc((unsigned)strlen(*ap) + 1, sizeof(char)); strcpy(cp, *ap); *ap2 = cp; } *ap2 = 0; return (0); } diff --git a/usr.bin/mail/cmdtab.c b/usr.bin/mail/cmdtab.c index 1acfc763b6ba..8c5ed43a03ae 100644 --- a/usr.bin/mail/cmdtab.c +++ b/usr.bin/mail/cmdtab.c @@ -1,118 +1,115 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "def.h" #include "extern.h" /* * Mail -- a mail program * * Define all of the command names and bindings. */ const struct cmd cmdtab[] = { /* msgmask msgflag */ /* command function argtype result & mask */ /* ------- -------- ------- ------ ------- */ { "next", next, NDMLIST, 0, MMNDEL }, { "alias", group, M|RAWLIST, 0, 1000 }, { "print", type, MSGLIST, 0, MMNDEL }, { "type", type, MSGLIST, 0, MMNDEL }, { "Type", Type, MSGLIST, 0, MMNDEL }, { "Print", Type, MSGLIST, 0, MMNDEL }, { "visual", visual, I|MSGLIST, 0, MMNORM }, { "top", top, MSGLIST, 0, MMNDEL }, { "touch", stouch, W|MSGLIST, 0, MMNDEL }, { "preserve", preserve, W|MSGLIST, 0, MMNDEL }, { "delete", deletecmd, W|P|MSGLIST, 0, MMNDEL }, { "dp", deltype, W|MSGLIST, 0, MMNDEL }, { "dt", deltype, W|MSGLIST, 0, MMNDEL }, { "undelete", undeletecmd, P|MSGLIST, MDELETED,MMNDEL }, { "unset", unset, M|RAWLIST, 1, 1000 }, { "mail", sendmail, R|M|I|STRLIST, 0, 0 }, { "mbox", mboxit, W|MSGLIST, 0, 0 }, { "more", more, MSGLIST, 0, MMNDEL }, { "page", more, MSGLIST, 0, MMNDEL }, { "More", More, MSGLIST, 0, MMNDEL }, { "Page", More, MSGLIST, 0, MMNDEL }, { "unread", unread, MSGLIST, 0, MMNDEL }, { "!", shell, I|STRLIST, 0, 0 }, { "copy", copycmd, M|STRLIST, 0, 0 }, { "chdir", schdir, M|RAWLIST, 0, 1 }, { "cd", schdir, M|RAWLIST, 0, 1 }, { "save", save, STRLIST, 0, 0 }, { "source", source, M|RAWLIST, 1, 1 }, { "set", set, M|RAWLIST, 0, 1000 }, { "shell", dosh, I|NOLIST, 0, 0 }, { "version", pversion, M|NOLIST, 0, 0 }, { "group", group, M|RAWLIST, 0, 1000 }, { "write", swrite, STRLIST, 0, 0 }, { "from", from, MSGLIST, 0, MMNORM }, { "file", file, T|M|RAWLIST, 0, 1 }, { "folder", file, T|M|RAWLIST, 0, 1 }, { "folders", folders, T|M|NOLIST, 0, 0 }, { "?", help, M|NOLIST, 0, 0 }, { "z", scroll, M|STRLIST, 0, 0 }, { "headers", headers, MSGLIST, 0, MMNDEL }, { "help", help, M|NOLIST, 0, 0 }, { "=", pdot, NOLIST, 0, 0 }, { "Reply", Respond, R|I|MSGLIST, 0, MMNDEL }, { "Respond", Respond, R|I|MSGLIST, 0, MMNDEL }, { "reply", respond, R|I|MSGLIST, 0, MMNDEL }, { "respond", respond, R|I|MSGLIST, 0, MMNDEL }, { "edit", editor, I|MSGLIST, 0, MMNORM }, { "echo", echo, M|RAWLIST, 0, 1000 }, { "quit", quitcmd, NOLIST, 0, 0 }, { "list", pcmdlist, M|NOLIST, 0, 0 }, { "xit", rexit, M|NOLIST, 0, 0 }, { "exit", rexit, M|NOLIST, 0, 0 }, { "size", messize, MSGLIST, 0, MMNDEL }, { "hold", preserve, W|MSGLIST, 0, MMNDEL }, { "if", ifcmd, F|M|RAWLIST, 1, 1 }, { "else", elsecmd, F|M|RAWLIST, 0, 0 }, { "endif", endifcmd, F|M|RAWLIST, 0, 0 }, { "alternates", alternates, M|RAWLIST, 0, 1000 }, { "ignore", igfield, M|RAWLIST, 0, 1000 }, { "discard", igfield, M|RAWLIST, 0, 1000 }, { "retain", retfield, M|RAWLIST, 0, 1000 }, { "saveignore", saveigfield, M|RAWLIST, 0, 1000 }, { "savediscard",saveigfield, M|RAWLIST, 0, 1000 }, { "saveretain", saveretfield, M|RAWLIST, 0, 1000 }, /* { "Header", Header, STRLIST, 0, 1000 }, */ { "core", core, M|NOLIST, 0, 0 }, { "#", null, M|NOLIST, 0, 0 }, { "clobber", clobber, M|RAWLIST, 0, 1 }, { "inc", inc, T|NOLIST, 0, 0 }, { 0, 0, 0, 0, 0 } }; diff --git a/usr.bin/mail/collect.c b/usr.bin/mail/collect.c index e9be5332cf24..0c310b15b234 100644 --- a/usr.bin/mail/collect.c +++ b/usr.bin/mail/collect.c @@ -1,728 +1,725 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include /* * Mail -- a mail program * * Collect input from standard input, handling * ~ escapes. */ #include "rcv.h" #include #include "extern.h" /* * Read a message from standard input and return a read file to it * or NULL on error. */ /* * The following hokiness with global variables is so that on * receipt of an interrupt signal, the partial message can be salted * away on dead.letter. */ static sig_t saveint; /* Previous SIGINT value */ static sig_t savehup; /* Previous SIGHUP value */ static sig_t savetstp; /* Previous SIGTSTP value */ static sig_t savettou; /* Previous SIGTTOU value */ static sig_t savettin; /* Previous SIGTTIN value */ static FILE *collf; /* File for saving away */ static int hadintr; /* Have seen one SIGINT so far */ static jmp_buf colljmp; /* To get back to work */ static int colljmp_p; /* whether to long jump */ static jmp_buf collabort; /* To end collection with error */ FILE * collect(struct header *hp, int printheaders) { FILE *fbuf; int lc, cc, escape, eofcount, fd, c, t; char linebuf[LINESIZE], tempname[PATHSIZE], *cp, getsub; sigset_t nset; int longline, lastlong, rc; /* So we don't make 2 or more lines out of a long input line. */ collf = NULL; /* * Start catching signals from here, but we're still die on interrupts * until we're in the main loop. */ (void)sigemptyset(&nset); (void)sigaddset(&nset, SIGINT); (void)sigaddset(&nset, SIGHUP); (void)sigprocmask(SIG_BLOCK, &nset, NULL); if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN) (void)signal(SIGINT, collint); if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN) (void)signal(SIGHUP, collhup); savetstp = signal(SIGTSTP, collstop); savettou = signal(SIGTTOU, collstop); savettin = signal(SIGTTIN, collstop); if (setjmp(collabort) || setjmp(colljmp)) { (void)rm(tempname); goto err; } (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); noreset++; (void)snprintf(tempname, sizeof(tempname), "%s/mail.RsXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (collf = Fdopen(fd, "w+")) == NULL) { warn("%s", tempname); goto err; } (void)rm(tempname); /* * If we are going to prompt for a subject, * refrain from printing a newline after * the headers (since some people mind). */ t = GTO|GSUBJECT|GCC|GNL; getsub = 0; if (hp->h_subject == NULL && value("interactive") != NULL && (value("ask") != NULL || value("asksub") != NULL)) t &= ~GNL, getsub++; if (printheaders) { puthead(hp, stdout, t); (void)fflush(stdout); } if ((cp = value("escape")) != NULL) escape = *cp; else escape = ESCAPE; eofcount = 0; hadintr = 0; longline = 0; if (!setjmp(colljmp)) { if (getsub) grabh(hp, GSUBJECT); } else { /* * Come here for printing the after-signal message. * Duplicate messages won't be printed because * the write is aborted if we get a SIGTTOU. */ cont: if (hadintr) { (void)fflush(stdout); fprintf(stderr, "\n(Interrupt -- one more to kill letter)\n"); } else { printf("(continue)\n"); (void)fflush(stdout); } } for (;;) { colljmp_p = 1; c = readline(stdin, linebuf, LINESIZE); colljmp_p = 0; if (c < 0) { if (value("interactive") != NULL && value("ignoreeof") != NULL && ++eofcount < 25) { printf("Use \".\" to terminate letter\n"); continue; } break; } lastlong = longline; longline = c == LINESIZE - 1; eofcount = 0; hadintr = 0; if (linebuf[0] == '.' && linebuf[1] == '\0' && value("interactive") != NULL && !lastlong && (value("dot") != NULL || value("ignoreeof") != NULL)) break; if (linebuf[0] != escape || value("interactive") == NULL || lastlong) { if (putline(collf, linebuf, !longline) < 0) goto err; continue; } c = linebuf[1]; switch (c) { default: /* * On double escape, just send the single one. * Otherwise, it's an error. */ if (c == escape) { if (putline(collf, &linebuf[1], !longline) < 0) goto err; else break; } printf("Unknown tilde escape.\n"); break; case 'C': /* * Dump core. */ core(NULL); break; case '!': /* * Shell escape, send the balance of the * line to sh -c. */ shell(&linebuf[2]); break; case ':': case '_': /* * Escape to command mode, but be nice! */ execute(&linebuf[2], 1); goto cont; case '.': /* * Simulate end of file on input. */ goto out; case 'q': /* * Force a quit of sending mail. * Act like an interrupt happened. */ hadintr++; collint(SIGINT); exit(1); case 'x': /* * Exit, do not save in dead.letter. */ goto err; case 'h': /* * Grab a bunch of headers. */ grabh(hp, GTO|GSUBJECT|GCC|GBCC); goto cont; case 't': /* * Add to the To list. */ hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO)); break; case 's': /* * Set the Subject line. */ cp = &linebuf[2]; while (isspace((unsigned char)*cp)) cp++; hp->h_subject = savestr(cp); break; case 'R': /* * Set the Reply-To line. */ cp = &linebuf[2]; while (isspace((unsigned char)*cp)) cp++; hp->h_replyto = savestr(cp); break; case 'c': /* * Add to the CC list. */ hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC)); break; case 'b': /* * Add to the BCC list. */ hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC)); break; case 'i': case 'A': case 'a': /* * Insert named variable in message. */ switch(c) { case 'i': cp = &linebuf[2]; while(isspace((unsigned char)*cp)) cp++; break; case 'a': cp = "sign"; break; case 'A': cp = "Sign"; break; default: goto err; } if(*cp != '\0' && (cp = value(cp)) != NULL) { printf("%s\n", cp); if(putline(collf, cp, 1) < 0) goto err; } break; case 'd': /* * Read in the dead letter file. */ if (strlcpy(linebuf + 2, getdeadletter(), sizeof(linebuf) - 2) >= sizeof(linebuf) - 2) { printf("Line buffer overflow\n"); break; } /* FALLTHROUGH */ case 'r': case '<': /* * Invoke a file: * Search for the file name, * then open it and copy the contents to collf. */ cp = &linebuf[2]; while (isspace((unsigned char)*cp)) cp++; if (*cp == '\0') { printf("Interpolate what file?\n"); break; } cp = expand(cp); if (cp == NULL) break; if (*cp == '!') { /* * Insert stdout of command. */ char *sh; int nullfd, tempfd, rc; char tempname2[PATHSIZE]; if ((nullfd = open(_PATH_DEVNULL, O_RDONLY, 0)) == -1) { warn(_PATH_DEVNULL); break; } (void)snprintf(tempname2, sizeof(tempname2), "%s/mail.ReXXXXXXXXXX", tmpdir); if ((tempfd = mkstemp(tempname2)) == -1 || (fbuf = Fdopen(tempfd, "w+")) == NULL) { warn("%s", tempname2); break; } (void)unlink(tempname2); if ((sh = value("SHELL")) == NULL) sh = _PATH_CSHELL; rc = run_command(sh, 0, nullfd, fileno(fbuf), "-c", cp+1, NULL); close(nullfd); if (rc < 0) { (void)Fclose(fbuf); break; } if (fsize(fbuf) == 0) { fprintf(stderr, "No bytes from command \"%s\"\n", cp+1); (void)Fclose(fbuf); break; } rewind(fbuf); } else if (isdir(cp)) { printf("%s: Directory\n", cp); break; } else if ((fbuf = Fopen(cp, "r")) == NULL) { warn("%s", cp); break; } printf("\"%s\" ", cp); (void)fflush(stdout); lc = 0; cc = 0; while ((rc = readline(fbuf, linebuf, LINESIZE)) >= 0) { if (rc != LINESIZE - 1) lc++; if ((t = putline(collf, linebuf, rc != LINESIZE - 1)) < 0) { (void)Fclose(fbuf); goto err; } cc += t; } (void)Fclose(fbuf); printf("%d/%d\n", lc, cc); break; case 'w': /* * Write the message on a file. */ cp = &linebuf[2]; while (*cp == ' ' || *cp == '\t') cp++; if (*cp == '\0') { fprintf(stderr, "Write what file!?\n"); break; } if ((cp = expand(cp)) == NULL) break; rewind(collf); exwrite(cp, collf, 1); break; case 'm': case 'M': case 'f': case 'F': /* * Interpolate the named messages, if we * are in receiving mail mode. Does the * standard list processing garbage. * If ~f is given, we don't shift over. */ if (forward(linebuf + 2, collf, tempname, c) < 0) goto err; goto cont; case '?': if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) { warn("%s", _PATH_TILDE); break; } while ((t = getc(fbuf)) != EOF) (void)putchar(t); (void)Fclose(fbuf); break; case 'p': /* * Print out the current state of the * message without altering anything. */ rewind(collf); printf("-------\nMessage contains:\n"); puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL); while ((t = getc(collf)) != EOF) (void)putchar(t); goto cont; case '|': /* * Pipe message through command. * Collect output as new message. */ rewind(collf); mespipe(collf, &linebuf[2]); goto cont; case 'v': case 'e': /* * Edit the current message. * 'e' means to use EDITOR * 'v' means to use VISUAL */ rewind(collf); mesedit(collf, c); goto cont; } } goto out; err: if (collf != NULL) { (void)Fclose(collf); collf = NULL; } out: if (collf != NULL) rewind(collf); noreset--; (void)sigprocmask(SIG_BLOCK, &nset, NULL); (void)signal(SIGINT, saveint); (void)signal(SIGHUP, savehup); (void)signal(SIGTSTP, savetstp); (void)signal(SIGTTOU, savettou); (void)signal(SIGTTIN, savettin); (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); return (collf); } /* * Write a file, ex-like if f set. */ int exwrite(char name[], FILE *fp, int f) { FILE *of; int c, lc; long cc; struct stat junk; if (f) { printf("\"%s\" ", name); (void)fflush(stdout); } if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) { if (!f) fprintf(stderr, "%s: ", name); fprintf(stderr, "File exists\n"); return (-1); } if ((of = Fopen(name, "w")) == NULL) { warn((char *)NULL); return (-1); } lc = 0; cc = 0; while ((c = getc(fp)) != EOF) { cc++; if (c == '\n') lc++; (void)putc(c, of); if (ferror(of)) { warnx("%s", name); (void)Fclose(of); return (-1); } } (void)Fclose(of); printf("%d/%ld\n", lc, cc); (void)fflush(stdout); return (0); } /* * Edit the message being collected on fp. * On return, make the edit file the new temp file. */ void mesedit(FILE *fp, int c) { sig_t sigint = signal(SIGINT, SIG_IGN); FILE *nf = run_editor(fp, (off_t)-1, c, 0); if (nf != NULL) { (void)fseeko(nf, (off_t)0, SEEK_END); collf = nf; (void)Fclose(fp); } (void)signal(SIGINT, sigint); } /* * Pipe the message through the command. * Old message is on stdin of command; * New message collected from stdout. * Sh -c must return 0 to accept the new message. */ void mespipe(FILE *fp, char cmd[]) { FILE *nf; int fd; sig_t sigint = signal(SIGINT, SIG_IGN); char *sh, tempname[PATHSIZE]; (void)snprintf(tempname, sizeof(tempname), "%s/mail.ReXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (nf = Fdopen(fd, "w+")) == NULL) { warn("%s", tempname); goto out; } (void)rm(tempname); /* * stdin = current message. * stdout = new message. */ if ((sh = value("SHELL")) == NULL) sh = _PATH_CSHELL; if (run_command(sh, 0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) { (void)Fclose(nf); goto out; } if (fsize(nf) == 0) { fprintf(stderr, "No bytes from \"%s\" !?\n", cmd); (void)Fclose(nf); goto out; } /* * Take new files. */ (void)fseeko(nf, (off_t)0, SEEK_END); collf = nf; (void)Fclose(fp); out: (void)signal(SIGINT, sigint); } /* * Interpolate the named messages into the current * message, preceding each line with a tab. * Return a count of the number of characters now in * the message, or -1 if an error is encountered writing * the message temporary. The flag argument is 'm' if we * should shift over and 'f' if not. */ int forward(char ms[], FILE *fp, char *fn, int f) { int *msgvec; struct ignoretab *ig; char *tabst; msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec)); if (msgvec == NULL) return (0); if (getmsglist(ms, msgvec, 0) < 0) return (0); if (*msgvec == 0) { *msgvec = first(0, MMNORM); if (*msgvec == 0) { printf("No appropriate messages\n"); return (0); } msgvec[1] = 0; } if (f == 'f' || f == 'F') tabst = NULL; else if ((tabst = value("indentprefix")) == NULL) tabst = "\t"; ig = isupper((unsigned char)f) ? NULL : ignore; printf("Interpolating:"); for (; *msgvec != 0; msgvec++) { struct message *mp = message + *msgvec - 1; touch(mp); printf(" %d", *msgvec); if (sendmessage(mp, fp, ig, tabst) < 0) { warnx("%s", fn); return (-1); } } printf("\n"); return (0); } /* * Print (continue) when continued after ^Z. */ /*ARGSUSED*/ void collstop(int s) { sig_t old_action = signal(s, SIG_DFL); sigset_t nset; (void)sigemptyset(&nset); (void)sigaddset(&nset, s); (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); (void)kill(0, s); (void)sigprocmask(SIG_BLOCK, &nset, NULL); (void)signal(s, old_action); if (colljmp_p) { colljmp_p = 0; hadintr = 0; longjmp(colljmp, 1); } } /* * On interrupt, come here to save the partial message in ~/dead.letter. * Then jump out of the collection loop. */ /*ARGSUSED*/ void collint(int s __unused) { /* * the control flow is subtle, because we can be called from ~q. */ if (!hadintr) { if (value("ignore") != NULL) { printf("@"); (void)fflush(stdout); clearerr(stdin); return; } hadintr = 1; longjmp(colljmp, 1); } rewind(collf); if (value("nosave") == NULL) savedeadletter(collf); longjmp(collabort, 1); } /*ARGSUSED*/ void collhup(int s __unused) { rewind(collf); savedeadletter(collf); /* * Let's pretend nobody else wants to clean up, * a true statement at this time. */ exit(1); } void savedeadletter(FILE *fp) { FILE *dbuf; int c; char *cp; if (fsize(fp) == 0) return; cp = getdeadletter(); c = umask(077); dbuf = Fopen(cp, "a"); (void)umask(c); if (dbuf == NULL) return; while ((c = getc(fp)) != EOF) (void)putc(c, dbuf); (void)Fclose(dbuf); rewind(fp); } diff --git a/usr.bin/mail/edit.c b/usr.bin/mail/edit.c index c07b98c8ff6d..1a03c5d4e186 100644 --- a/usr.bin/mail/edit.c +++ b/usr.bin/mail/edit.c @@ -1,211 +1,208 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include #include "extern.h" /* * Mail -- a mail program * * Perform message editing functions. */ /* * Edit a message list. */ int editor(void *msgvec) { return (edit1(msgvec, 'e')); } /* * Invoke the visual editor on a message list. */ int visual(void *msgvec) { return (edit1(msgvec, 'v')); } /* * Edit a message by writing the message into a funnily-named file * (which should not exist) and forking an editor on it. * We get the editor from the stuff above. */ int edit1(int *msgvec, int type) { int c, i; FILE *fp; struct message *mp; off_t size; /* * Deal with each message to be edited . . . */ for (i = 0; i < msgCount && msgvec[i]; i++) { sig_t sigint; if (i > 0) { char buf[100]; char *p; printf("Edit message %d [ynq]? ", msgvec[i]); if (fgets(buf, sizeof(buf), stdin) == NULL) break; for (p = buf; *p == ' ' || *p == '\t'; p++) ; if (*p == 'q') break; if (*p == 'n') continue; } dot = mp = &message[msgvec[i] - 1]; touch(mp); sigint = signal(SIGINT, SIG_IGN); fp = run_editor(setinput(mp), mp->m_size, type, readonly); if (fp != NULL) { (void)fseeko(otf, (off_t)0, SEEK_END); size = ftello(otf); mp->m_block = blockof(size); mp->m_offset = boffsetof(size); mp->m_size = (long)fsize(fp); mp->m_lines = 0; mp->m_flag |= MODIFY; rewind(fp); while ((c = getc(fp)) != EOF) { if (c == '\n') mp->m_lines++; if (putc(c, otf) == EOF) break; } if (ferror(otf)) warnx("/tmp"); (void)Fclose(fp); } (void)signal(SIGINT, sigint); } return (0); } /* * Run an editor on the file at "fpp" of "size" bytes, * and return a new file pointer. * Signals must be handled by the caller. * "Type" is 'e' for _PATH_EX, 'v' for _PATH_VI. */ FILE * run_editor(FILE *fp, off_t size, int type, int readonly) { FILE *nf = NULL; int t; time_t modtime; char *edit, tempname[PATHSIZE]; struct stat statb; (void)snprintf(tempname, sizeof(tempname), "%s/mail.ReXXXXXXXXXX", tmpdir); if ((t = mkstemp(tempname)) == -1 || (nf = Fdopen(t, "w")) == NULL) { warn("%s", tempname); goto out; } if (readonly && fchmod(t, 0400) == -1) { warn("%s", tempname); (void)rm(tempname); goto out; } if (size >= 0) while (--size >= 0 && (t = getc(fp)) != EOF) (void)putc(t, nf); else while ((t = getc(fp)) != EOF) (void)putc(t, nf); (void)fflush(nf); if (fstat(fileno(nf), &statb) < 0) modtime = 0; else modtime = statb.st_mtime; if (ferror(nf)) { (void)Fclose(nf); warnx("%s", tempname); (void)rm(tempname); nf = NULL; goto out; } if (Fclose(nf) < 0) { warn("%s", tempname); (void)rm(tempname); nf = NULL; goto out; } nf = NULL; if ((edit = value(type == 'e' ? "EDITOR" : "VISUAL")) == NULL) edit = type == 'e' ? _PATH_EX : _PATH_VI; if (run_command(edit, 0, -1, -1, tempname, NULL) < 0) { (void)rm(tempname); goto out; } /* * If in read only mode or file unchanged, just remove the editor * temporary and return. */ if (readonly) { (void)rm(tempname); goto out; } if (stat(tempname, &statb) < 0) { warn("%s", tempname); goto out; } if (modtime == statb.st_mtime) { (void)rm(tempname); goto out; } /* * Now switch to new file. */ if ((nf = Fopen(tempname, "a+")) == NULL) { warn("%s", tempname); (void)rm(tempname); goto out; } (void)rm(tempname); out: return (nf); } diff --git a/usr.bin/mail/fio.c b/usr.bin/mail/fio.c index 59c6e912bd86..6a31f2a5e361 100644 --- a/usr.bin/mail/fio.c +++ b/usr.bin/mail/fio.c @@ -1,448 +1,445 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include #include #include #include #include #include "extern.h" /* * Mail -- a mail program * * File I/O. */ extern int wait_status; /* * Set up the input pointers while copying the mail file into /tmp. */ void setptr(FILE *ibuf, off_t offset) { int c, count; char *cp, *cp2; struct message this; FILE *mestmp; int maybe, inhead; char linebuf[LINESIZE], pathbuf[PATHSIZE]; int omsgCount; /* Get temporary file. */ (void)snprintf(pathbuf, sizeof(pathbuf), "%s/mail.XXXXXXXXXX", tmpdir); if ((c = mkstemp(pathbuf)) == -1 || (mestmp = Fdopen(c, "r+")) == NULL) err(1, "can't open %s", pathbuf); (void)rm(pathbuf); if (offset == 0) { msgCount = 0; } else { /* Seek into the file to get to the new messages */ (void)fseeko(ibuf, offset, SEEK_SET); /* * We need to make "offset" a pointer to the end of * the temp file that has the copy of the mail file. * If any messages have been edited, this will be * different from the offset into the mail file. */ (void)fseeko(otf, (off_t)0, SEEK_END); offset = ftello(otf); } omsgCount = msgCount; maybe = 1; inhead = 0; this.m_flag = MUSED|MNEW; this.m_size = 0; this.m_lines = 0; this.m_block = 0; this.m_offset = 0; for (;;) { if (fgets(linebuf, sizeof(linebuf), ibuf) == NULL) { if (append(&this, mestmp)) errx(1, "temporary file"); makemessage(mestmp, omsgCount); return; } count = strlen(linebuf); /* * Transforms lines ending in to just . * This allows mail to be able to read Eudora mailboxes. */ if (count >= 2 && linebuf[count - 1] == '\n' && linebuf[count - 2] == '\r') { count--; linebuf[count - 1] = '\n'; } (void)fwrite(linebuf, sizeof(*linebuf), count, otf); if (ferror(otf)) errx(1, "/tmp"); if (count) linebuf[count - 1] = '\0'; if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { msgCount++; if (append(&this, mestmp)) errx(1, "temporary file"); this.m_flag = MUSED|MNEW; this.m_size = 0; this.m_lines = 0; this.m_block = blockof(offset); this.m_offset = boffsetof(offset); inhead = 1; } else if (linebuf[0] == 0) { inhead = 0; } else if (inhead) { for (cp = linebuf, cp2 = "status";; cp++) { if ((c = *cp2++) == '\0') { while (isspace((unsigned char)*cp++)) ; if (cp[-1] != ':') break; while ((c = *cp++) != '\0') if (c == 'R') this.m_flag |= MREAD; else if (c == 'O') this.m_flag &= ~MNEW; inhead = 0; break; } if (*cp != c && *cp != toupper((unsigned char)c)) break; } } offset += count; this.m_size += count; this.m_lines++; maybe = linebuf[0] == 0; } } /* * Drop the passed line onto the passed output buffer. * If a write error occurs, return -1, else the count of * characters written, including the newline if requested. */ int putline(FILE *obuf, char *linebuf, int outlf) { int c; c = strlen(linebuf); (void)fwrite(linebuf, sizeof(*linebuf), c, obuf); if (outlf) { fprintf(obuf, "\n"); c++; } if (ferror(obuf)) return (-1); return (c); } /* * Read up a line from the specified input into the line * buffer. Return the number of characters read. Do not * include the newline (or carriage return) at the end. */ int readline(FILE *ibuf, char *linebuf, int linesize) { int n; clearerr(ibuf); if (fgets(linebuf, linesize, ibuf) == NULL) return (-1); n = strlen(linebuf); if (n > 0 && linebuf[n - 1] == '\n') linebuf[--n] = '\0'; if (n > 0 && linebuf[n - 1] == '\r') linebuf[--n] = '\0'; return (n); } /* * Return a file buffer all ready to read up the * passed message pointer. */ FILE * setinput(struct message *mp) { (void)fflush(otf); if (fseeko(itf, positionof(mp->m_block, mp->m_offset), SEEK_SET) < 0) err(1, "fseeko"); return (itf); } /* * Take the data out of the passed ghost file and toss it into * a dynamically allocated message structure. */ void makemessage(FILE *f, int omsgCount) { size_t size; struct message *nmessage; size = (msgCount + 1) * sizeof(struct message); nmessage = (struct message *)realloc(message, size); if (nmessage == NULL) errx(1, "Insufficient memory for %d messages\n", msgCount); if (omsgCount == 0 || message == NULL) dot = nmessage; else dot = nmessage + (dot - message); message = nmessage; size -= (omsgCount + 1) * sizeof(struct message); (void)fflush(f); (void)lseek(fileno(f), (off_t)sizeof(*message), 0); if (read(fileno(f), (void *)&message[omsgCount], size) != size) errx(1, "Message temporary file corrupted"); message[msgCount].m_size = 0; message[msgCount].m_lines = 0; (void)Fclose(f); } /* * Append the passed message descriptor onto the temp file. * If the write fails, return 1, else 0 */ int append(struct message *mp, FILE *f) { return (fwrite((char *)mp, sizeof(*mp), 1, f) != 1); } /* * Delete a file, but only if the file is a plain file. */ int rm(char *name) { struct stat sb; if (stat(name, &sb) < 0) return (-1); if (!S_ISREG(sb.st_mode)) { errno = EISDIR; return (-1); } return (unlink(name)); } static int sigdepth; /* depth of holdsigs() */ static sigset_t nset, oset; /* * Hold signals SIGHUP, SIGINT, and SIGQUIT. */ void holdsigs(void) { if (sigdepth++ == 0) { (void)sigemptyset(&nset); (void)sigaddset(&nset, SIGHUP); (void)sigaddset(&nset, SIGINT); (void)sigaddset(&nset, SIGQUIT); (void)sigprocmask(SIG_BLOCK, &nset, &oset); } } /* * Release signals SIGHUP, SIGINT, and SIGQUIT. */ void relsesigs(void) { if (--sigdepth == 0) (void)sigprocmask(SIG_SETMASK, &oset, NULL); } /* * Determine the size of the file possessed by * the passed buffer. */ off_t fsize(FILE *iob) { struct stat sbuf; if (fstat(fileno(iob), &sbuf) < 0) return (0); return (sbuf.st_size); } /* * Evaluate the string given as a new mailbox name. * Supported meta characters: * % for my system mail box * %user for user's system mail box * # for previous file * & invoker's mbox file * +file file in folder directory * any shell meta character * Return the file name as a dynamic string. */ char * expand(char *name) { char xname[PATHSIZE]; char cmdbuf[PATHSIZE]; /* also used for file names */ int pid, l; char *cp, *sh; int pivec[2]; struct stat sbuf; /* * The order of evaluation is "%" and "#" expand into constants. * "&" can expand into "+". "+" can expand into shell meta characters. * Shell meta characters expand into constants. * This way, we make no recursive expansion. */ switch (*name) { case '%': findmail(name[1] ? name + 1 : myname, xname, sizeof(xname)); return (savestr(xname)); case '#': if (name[1] != 0) break; if (prevfile[0] == 0) { printf("No previous file\n"); return (NULL); } return (savestr(prevfile)); case '&': if (name[1] == 0 && (name = value("MBOX")) == NULL) name = "~/mbox"; /* fall through */ } if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) { (void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1); name = savestr(xname); } /* catch the most common shell meta character */ if (name[0] == '~' && homedir != NULL && (name[1] == '/' || name[1] == '\0')) { (void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1); name = savestr(xname); } if (!strpbrk(name, "~{[*?$`'\"\\")) return (savestr(name)); if (pipe(pivec) < 0) { warn("pipe"); return (NULL); } (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name); if ((sh = value("SHELL")) == NULL) sh = _PATH_CSHELL; pid = start_command(sh, 0, -1, pivec[1], "-c", cmdbuf, NULL); if (pid < 0) { (void)close(pivec[0]); (void)close(pivec[1]); return (NULL); } (void)close(pivec[1]); l = read(pivec[0], xname, BUFSIZ); (void)close(pivec[0]); if (wait_child(pid) < 0 && WIFSIGNALED(wait_status) && WTERMSIG(wait_status) != SIGPIPE) { fprintf(stderr, "\"%s\": Expansion failed.\n", name); return (NULL); } if (l < 0) { warn("read"); return (NULL); } if (l == 0) { fprintf(stderr, "\"%s\": No match.\n", name); return (NULL); } if (l == BUFSIZ) { fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); return (NULL); } xname[l] = '\0'; for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) ; cp[1] = '\0'; if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) { fprintf(stderr, "\"%s\": Ambiguous.\n", name); return (NULL); } return (savestr(xname)); } /* * Determine the current folder directory name. */ int getfold(char *name, int namelen) { char *folder; int copylen; if ((folder = value("folder")) == NULL) return (-1); if (*folder == '/') copylen = strlcpy(name, folder, namelen); else copylen = snprintf(name, namelen, "%s/%s", homedir ? homedir : ".", folder); return (copylen < 0 || copylen >= namelen ? (-1) : (0)); } /* * Return the name of the dead.letter file. */ char * getdeadletter(void) { char *cp; if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL) cp = expand("~/dead.letter"); else if (*cp != '/') { char buf[PATHSIZE]; (void)snprintf(buf, sizeof(buf), "~/%s", cp); cp = expand(buf); } return (cp); } diff --git a/usr.bin/mail/getname.c b/usr.bin/mail/getname.c index a7bd0b9d22b9..85721b1d16ae 100644 --- a/usr.bin/mail/getname.c +++ b/usr.bin/mail/getname.c @@ -1,66 +1,63 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include #include "extern.h" /* Getname / getuserid for those with hashed passwd data base). */ /* * Search the passwd file for a uid. Return name on success, NULL on failure. */ char * getname(uid_t uid) { struct passwd *pw; if ((pw = getpwuid(uid)) == NULL) return (NULL); return (pw->pw_name); } /* * Convert the passed name to a user id and return it. Return -1 * on error. */ uid_t getuserid(char name[]) { struct passwd *pw; if ((pw = getpwnam(name)) == NULL) return (-1); return (pw->pw_uid); } diff --git a/usr.bin/mail/head.c b/usr.bin/mail/head.c index ad450765d722..2e27581c9d5d 100644 --- a/usr.bin/mail/head.c +++ b/usr.bin/mail/head.c @@ -1,272 +1,269 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include "extern.h" /* * Mail -- a mail program * * Routines for processing and detecting headlines. */ /* * See if the passed line buffer is a mail header. * Return true if yes. Note the extreme pains to * accommodate all funny formats. */ int ishead(char linebuf[]) { struct headline hl; char parbuf[BUFSIZ]; if (strncmp(linebuf, "From ", 5) != 0) return (0); parse(linebuf, &hl, parbuf); if (hl.l_date == NULL) { fail(linebuf, "No date field"); return (0); } if (!isdate(hl.l_date)) { fail(linebuf, "Date field not legal date"); return (0); } /* * I guess we got it! */ return (1); } void fail(const char *linebuf __unused, const char *reason __unused) { /* if (value("debug") == NULL) return; fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason); */ } /* * Split a headline into its useful components. * Copy the line into dynamic string space, then set * pointers into the copied line in the passed headline * structure. Actually, it scans. */ void parse(char line[], struct headline *hl, char pbuf[]) { char *cp, *sp; char word[LINESIZE]; hl->l_from = NULL; hl->l_tty = NULL; hl->l_date = NULL; cp = line; sp = pbuf; /* * Skip over "From" first. */ cp = nextword(cp, word); /* * Check for missing return-path. */ if (isdate(cp)) { hl->l_date = copyin(cp, &sp); return; } cp = nextword(cp, word); if (strlen(word) > 0) hl->l_from = copyin(word, &sp); if (cp != NULL && strncmp(cp, "tty", 3) == 0) { cp = nextword(cp, word); hl->l_tty = copyin(word, &sp); } if (cp != NULL) hl->l_date = copyin(cp, &sp); } /* * Copy the string on the left into the string on the right * and bump the right (reference) string pointer by the length. * Thus, dynamically allocate space in the right string, copying * the left string into it. */ char * copyin(char *src, char **space) { char *cp, *top; top = cp = *space; while ((*cp++ = *src++) != '\0') ; *space = cp; return (top); } /* * Test to see if the passed string is a ctime(3) generated * date string as documented in the manual. The template * below is used as the criterion of correctness. * Also, we check for a possible trailing time zone using * the tmztype template. * * If the mail file is created by Sys V (Solaris), there are * no seconds in the time. If the mail is created by another * program such as imapd, it might have timezone as * <-|+>nnnn (-0800 for instance) at the end. */ /* * 'A' An upper case char * 'a' A lower case char * ' ' A space * '0' A digit * 'O' A digit or space * 'p' A punctuation char * 'P' A punctuation char or space * ':' A colon * 'N' A new line */ static char *date_formats[] = { "Aaa Aaa O0 00:00:00 0000", /* Mon Jan 01 23:59:59 2001 */ "Aaa Aaa O0 00:00:00 AAA 0000", /* Mon Jan 01 23:59:59 PST 2001 */ "Aaa Aaa O0 00:00:00 0000 p0000", /* Mon Jan 01 23:59:59 2001 -0800 */ "Aaa Aaa O0 00:00 0000", /* Mon Jan 01 23:59 2001 */ "Aaa Aaa O0 00:00 AAA 0000", /* Mon Jan 01 23:59 PST 2001 */ "Aaa Aaa O0 00:00 0000 p0000", /* Mon Jan 01 23:59 2001 -0800 */ NULL }; int isdate(char date[]) { int i; for(i = 0; date_formats[i] != NULL; i++) { if (cmatch(date, date_formats[i])) return (1); } return (0); } /* * Match the given string (cp) against the given template (tp). * Return 1 if they match, 0 if they don't */ int cmatch(char *cp, char *tp) { while (*cp != '\0' && *tp != '\0') switch (*tp++) { case 'a': if (!islower((unsigned char)*cp++)) return (0); break; case 'A': if (!isupper((unsigned char)*cp++)) return (0); break; case ' ': if (*cp++ != ' ') return (0); break; case '0': if (!isdigit((unsigned char)*cp++)) return (0); break; case 'O': if (*cp != ' ' && !isdigit((unsigned char)*cp)) return (0); cp++; break; case 'p': if (!ispunct((unsigned char)*cp++)) return (0); break; case 'P': if (*cp != ' ' && !ispunct((unsigned char)*cp)) return (0); cp++; break; case ':': if (*cp++ != ':') return (0); break; case 'N': if (*cp++ != '\n') return (0); break; } if (*cp != '\0' || *tp != '\0') return (0); return (1); } /* * Collect a liberal (space, tab delimited) word into the word buffer * passed. Also, return a pointer to the next word following that, * or NULL if none follow. */ char * nextword(char *wp, char *wbuf) { int c; if (wp == NULL) { *wbuf = '\0'; return (NULL); } while ((c = *wp++) != '\0' && c != ' ' && c != '\t') { *wbuf++ = c; if (c == '"') { while ((c = *wp++) != '\0' && c != '"') *wbuf++ = c; if (c == '"') *wbuf++ = c; else wp--; } } *wbuf = '\0'; for (; c == ' ' || c == '\t'; c = *wp++) ; if (c == '\0') return (NULL); return (wp - 1); } diff --git a/usr.bin/mail/lex.c b/usr.bin/mail/lex.c index 7c579e5a4743..1043dd2d4706 100644 --- a/usr.bin/mail/lex.c +++ b/usr.bin/mail/lex.c @@ -1,696 +1,693 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include #include #include "extern.h" /* * Mail -- a mail program * * Lexical processing of commands. */ static const char *prompt = "& "; extern const struct cmd cmdtab[]; extern const char *version; /* * Set up editing on the given file name. * If the first character of name is %, we are considered to be * editing the file, otherwise we are reading our mail which has * signficance for mbox and so forth. * * If the -e option is being passed to mail, this function has a * tri-state return code: -1 on error, 0 on no mail, 1 if there is * mail. */ int setfile(char *name) { FILE *ibuf; int checkmode, i, fd; struct stat stb; char isedit = *name != '%' || getuserid(myname) != getuid(); char *who = name[1] ? name + 1 : myname; char tempname[PATHSIZE]; static int shudclob; checkmode = value("checkmode") != NULL; if ((name = expand(name)) == NULL) return (-1); if ((ibuf = Fopen(name, "r")) == NULL) { if (!isedit && errno == ENOENT) goto nomail; warn("%s", name); return (-1); } if (fstat(fileno(ibuf), &stb) < 0) { warn("fstat"); (void)Fclose(ibuf); return (-1); } if (S_ISDIR(stb.st_mode) || !S_ISREG(stb.st_mode)) { (void)Fclose(ibuf); errno = S_ISDIR(stb.st_mode) ? EISDIR : EINVAL; warn("%s", name); return (-1); } /* * Looks like all will be well. We must now relinquish our * hold on the current set of stuff. Must hold signals * while we are reading the new file, else we will ruin * the message[] data structure. */ holdsigs(); if (shudclob) quit(); /* * Copy the messages into /tmp * and set pointers. */ readonly = 0; if ((i = open(name, 1)) < 0) readonly++; else (void)close(i); if (shudclob) { (void)fclose(itf); (void)fclose(otf); } shudclob = 1; edit = isedit; strlcpy(prevfile, mailname, sizeof(prevfile)); if (name != mailname) strlcpy(mailname, name, sizeof(mailname)); mailsize = fsize(ibuf); (void)snprintf(tempname, sizeof(tempname), "%s/mail.RxXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (otf = fdopen(fd, "w")) == NULL) err(1, "%s", tempname); (void)fcntl(fileno(otf), F_SETFD, 1); if ((itf = fopen(tempname, "r")) == NULL) err(1, "%s", tempname); (void)fcntl(fileno(itf), F_SETFD, 1); (void)rm(tempname); setptr(ibuf, 0); setmsize(msgCount); /* * New mail may have arrived while we were reading * the mail file, so reset mailsize to be where * we really are in the file... */ mailsize = ftello(ibuf); (void)Fclose(ibuf); relsesigs(); sawcom = 0; if ((checkmode || !edit) && msgCount == 0) { nomail: if (!checkmode) { fprintf(stderr, "No mail for %s\n", who); return (-1); } else return (0); } return (checkmode ? 1 : 0); } /* * Incorporate any new mail that has arrived since we first * started reading mail. */ int incfile(void) { off_t newsize; int omsgCount = msgCount; FILE *ibuf; ibuf = Fopen(mailname, "r"); if (ibuf == NULL) return (-1); holdsigs(); newsize = fsize(ibuf); if (newsize == 0) return (-1); /* mail box is now empty??? */ if (newsize < mailsize) return (-1); /* mail box has shrunk??? */ if (newsize == mailsize) return (0); /* no new mail */ setptr(ibuf, mailsize); setmsize(msgCount); mailsize = ftello(ibuf); (void)Fclose(ibuf); relsesigs(); return (msgCount - omsgCount); } static int *msgvec; static int reset_on_stop; /* do a reset() if stopped */ /* * Interpret user commands one by one. If standard input is not a tty, * print no prompt. */ void commands(void) { int n, eofloop = 0; char linebuf[LINESIZE]; if (!sourcing) { if (signal(SIGINT, SIG_IGN) != SIG_IGN) (void)signal(SIGINT, intr); if (signal(SIGHUP, SIG_IGN) != SIG_IGN) (void)signal(SIGHUP, hangup); (void)signal(SIGTSTP, stop); (void)signal(SIGTTOU, stop); (void)signal(SIGTTIN, stop); } setexit(); for (;;) { /* * Print the prompt, if needed. Clear out * string space, and flush the output. */ if (!sourcing && value("interactive") != NULL) { if ((value("autoinc") != NULL) && (incfile() > 0)) printf("New mail has arrived.\n"); reset_on_stop = 1; printf("%s", prompt); } (void)fflush(stdout); sreset(); /* * Read a line of commands from the current input * and handle end of file specially. */ n = 0; for (;;) { if (readline(input, &linebuf[n], LINESIZE - n) < 0) { if (n == 0) n = -1; break; } if ((n = strlen(linebuf)) == 0) break; n--; if (linebuf[n] != '\\') break; linebuf[n++] = ' '; } reset_on_stop = 0; if (n < 0) { /* eof */ if (loading) break; if (sourcing) { unstack(); continue; } if (value("interactive") != NULL && value("ignoreeof") != NULL && ++eofloop < 25) { printf("Use \"quit\" to quit.\n"); continue; } break; } eofloop = 0; if (execute(linebuf, 0)) break; } } /* * Execute a single command. * Command functions return 0 for success, 1 for error, and -1 * for abort. A 1 or -1 aborts a load or source. A -1 aborts * the interactive command loop. * Contxt is non-zero if called while composing mail. */ int execute(char linebuf[], int contxt) { char word[LINESIZE]; char *arglist[MAXARGC]; const struct cmd *com; char *cp, *cp2; int c, muvec[2]; int e = 1; /* * Strip the white space away from the beginning * of the command, then scan out a word, which * consists of anything except digits and white space. * * Handle ! escapes differently to get the correct * lexical conventions. */ for (cp = linebuf; isspace((unsigned char)*cp); cp++) ; if (*cp == '!') { if (sourcing) { printf("Can't \"!\" while sourcing\n"); goto out; } shell(cp+1); return (0); } cp2 = word; while (*cp != '\0' && strchr(" \t0123456789$^.:/-+*'\"", *cp) == NULL) *cp2++ = *cp++; *cp2 = '\0'; /* * Look up the command; if not found, bitch. * Normally, a blank command would map to the * first command in the table; while sourcing, * however, we ignore blank lines to eliminate * confusion. */ if (sourcing && *word == '\0') return (0); com = lex(word); if (com == NULL) { printf("Unknown command: \"%s\"\n", word); goto out; } /* * See if we should execute the command -- if a conditional * we always execute it, otherwise, check the state of cond. */ if ((com->c_argtype & F) == 0) if ((cond == CRCV && !rcvmode) || (cond == CSEND && rcvmode)) return (0); /* * Process the arguments to the command, depending * on the type he expects. Default to an error. * If we are sourcing an interactive command, it's * an error. */ if (!rcvmode && (com->c_argtype & M) == 0) { printf("May not execute \"%s\" while sending\n", com->c_name); goto out; } if (sourcing && com->c_argtype & I) { printf("May not execute \"%s\" while sourcing\n", com->c_name); goto out; } if (readonly && com->c_argtype & W) { printf("May not execute \"%s\" -- message file is read only\n", com->c_name); goto out; } if (contxt && com->c_argtype & R) { printf("Cannot recursively invoke \"%s\"\n", com->c_name); goto out; } switch (com->c_argtype & ~(F|P|I|M|T|W|R)) { case MSGLIST: /* * A message list defaulting to nearest forward * legal message. */ if (msgvec == 0) { printf("Illegal use of \"message list\"\n"); break; } if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0) break; if (c == 0) { *msgvec = first(com->c_msgflag, com->c_msgmask); msgvec[1] = 0; } if (*msgvec == 0) { printf("No applicable messages\n"); break; } e = (*com->c_func)(msgvec); break; case NDMLIST: /* * A message list with no defaults, but no error * if none exist. */ if (msgvec == 0) { printf("Illegal use of \"message list\"\n"); break; } if (getmsglist(cp, msgvec, com->c_msgflag) < 0) break; e = (*com->c_func)(msgvec); break; case STRLIST: /* * Just the straight string, with * leading blanks removed. */ while (isspace((unsigned char)*cp)) cp++; e = (*com->c_func)(cp); break; case RAWLIST: /* * A vector of strings, in shell style. */ if ((c = getrawlist(cp, arglist, sizeof(arglist) / sizeof(*arglist))) < 0) break; if (c < com->c_minargs) { printf("%s requires at least %d arg(s)\n", com->c_name, com->c_minargs); break; } if (c > com->c_maxargs) { printf("%s takes no more than %d arg(s)\n", com->c_name, com->c_maxargs); break; } e = (*com->c_func)(arglist); break; case NOLIST: /* * Just the constant zero, for exiting, * eg. */ e = (*com->c_func)(0); break; default: errx(1, "Unknown argtype"); } out: /* * Exit the current source file on * error. */ if (e) { if (e < 0) return (1); if (loading) return (1); if (sourcing) unstack(); return (0); } if (com == NULL) return (0); if (value("autoprint") != NULL && com->c_argtype & P) if ((dot->m_flag & MDELETED) == 0) { muvec[0] = dot - &message[0] + 1; muvec[1] = 0; type(muvec); } if (!sourcing && (com->c_argtype & T) == 0) sawcom = 1; return (0); } /* * Set the size of the message vector used to construct argument * lists to message list functions. */ void setmsize(int sz) { if (msgvec != NULL) (void)free(msgvec); msgvec = calloc((unsigned)(sz + 1), sizeof(*msgvec)); } /* * Find the correct command in the command table corresponding * to the passed command "word" */ const struct cmd * lex(char word[]) { const struct cmd *cp; /* * ignore trailing chars after `#' * * lines with beginning `#' are comments * spaces before `#' are ignored in execute() */ if (*word == '#') *(word+1) = '\0'; for (cp = &cmdtab[0]; cp->c_name != NULL; cp++) if (isprefix(word, cp->c_name)) return (cp); return (NULL); } /* * Determine if as1 is a valid prefix of as2. * Return true if yep. */ int isprefix(const char *as1, const char *as2) { const char *s1, *s2; s1 = as1; s2 = as2; while (*s1++ == *s2) if (*s2++ == '\0') return (1); return (*--s1 == '\0'); } /* * The following gets called on receipt of an interrupt. This is * to abort printout of a command, mainly. * Dispatching here when command() is inactive crashes rcv. * Close all open files except 0, 1, 2, and the temporary. * Also, unstack all source files. */ static int inithdr; /* am printing startup headers */ void intr(int s __unused) { noreset = 0; if (!inithdr) sawcom++; inithdr = 0; while (sourcing) unstack(); close_all_files(); if (image >= 0) { (void)close(image); image = -1; } fprintf(stderr, "Interrupt\n"); reset(0); } /* * When we wake up after ^Z, reprint the prompt. */ void stop(int s) { sig_t old_action = signal(s, SIG_DFL); sigset_t nset; (void)sigemptyset(&nset); (void)sigaddset(&nset, s); (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); (void)kill(0, s); (void)sigprocmask(SIG_BLOCK, &nset, NULL); (void)signal(s, old_action); if (reset_on_stop) { reset_on_stop = 0; reset(0); } } /* * Branch here on hangup signal and simulate "exit". */ void hangup(int s __unused) { /* nothing to do? */ exit(1); } /* * Announce the presence of the current Mail version, * give the message count, and print a header listing. */ void announce(void) { int vec[2], mdot; mdot = newfileinfo(0); vec[0] = mdot; vec[1] = 0; dot = &message[mdot - 1]; if (msgCount > 0 && value("noheader") == NULL) { inithdr++; headers(vec); inithdr = 0; } } /* * Announce information about the file we are editing. * Return a likely place to set dot. */ int newfileinfo(int omsgCount) { struct message *mp; int u, n, mdot, d, s; char fname[PATHSIZE+1], zname[PATHSIZE+1], *ename; for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++) if (mp->m_flag & MNEW) break; if (mp >= &message[msgCount]) for (mp = &message[omsgCount]; mp < &message[msgCount]; mp++) if ((mp->m_flag & MREAD) == 0) break; if (mp < &message[msgCount]) mdot = mp - &message[0] + 1; else mdot = omsgCount + 1; s = d = 0; for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) { if (mp->m_flag & MNEW) n++; if ((mp->m_flag & MREAD) == 0) u++; if (mp->m_flag & MDELETED) d++; if (mp->m_flag & MSAVED) s++; } ename = mailname; if (getfold(fname, sizeof(fname) - 1) >= 0) { strcat(fname, "/"); if (strncmp(fname, mailname, strlen(fname)) == 0) { (void)snprintf(zname, sizeof(zname), "+%s", mailname + strlen(fname)); ename = zname; } } printf("\"%s\": ", ename); if (msgCount == 1) printf("1 message"); else printf("%d messages", msgCount); if (n > 0) printf(" %d new", n); if (u-n > 0) printf(" %d unread", u); if (d > 0) printf(" %d deleted", d); if (s > 0) printf(" %d saved", s); if (readonly) printf(" [Read only]"); printf("\n"); return (mdot); } /* * Print the current version number. */ int pversion(void *arg __unused) { printf("Version %s\n", version); return (0); } /* * Load a file of user definitions. */ void load(char *name) { FILE *in, *oldin; if ((in = Fopen(name, "r")) == NULL) return; oldin = input; input = in; loading = 1; sourcing = 1; commands(); loading = 0; sourcing = 0; input = oldin; (void)Fclose(in); } diff --git a/usr.bin/mail/list.c b/usr.bin/mail/list.c index bb2172c92a84..6d15a65e7662 100644 --- a/usr.bin/mail/list.c +++ b/usr.bin/mail/list.c @@ -1,813 +1,810 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include #include "extern.h" /* * Mail -- a mail program * * Message list handling. */ /* * Convert the user string of message numbers and * store the numbers into vector. * * Returns the count of messages picked up or -1 on error. */ int getmsglist(char *buf, int *vector, int flags) { int *ip; struct message *mp; if (msgCount == 0) { *vector = 0; return (0); } if (markall(buf, flags) < 0) return (-1); ip = vector; for (mp = &message[0]; mp < &message[msgCount]; mp++) if (mp->m_flag & MMARK) *ip++ = mp - &message[0] + 1; *ip = 0; return (ip - vector); } /* * Mark all messages that the user wanted from the command * line in the message structure. Return 0 on success, -1 * on error. */ /* * Bit values for colon modifiers. */ #define CMNEW 01 /* New messages */ #define CMOLD 02 /* Old messages */ #define CMUNREAD 04 /* Unread messages */ #define CMDELETED 010 /* Deleted messages */ #define CMREAD 020 /* Read messages */ /* * The following table describes the letters which can follow * the colon and gives the corresponding modifier bit. */ static struct coltab { char co_char; /* What to find past : */ int co_bit; /* Associated modifier bit */ int co_mask; /* m_status bits to mask */ int co_equal; /* ... must equal this */ } coltab[] = { { 'n', CMNEW, MNEW, MNEW }, { 'o', CMOLD, MNEW, 0 }, { 'u', CMUNREAD, MREAD, 0 }, { 'd', CMDELETED, MDELETED, MDELETED}, { 'r', CMREAD, MREAD, MREAD }, { 0, 0, 0, 0 } }; static int lastcolmod; int markall(char buf[], int f) { char **np; int i; struct message *mp; char *namelist[NMLSIZE], *bufp; int tok, beg, mc, star, other, valdot, colmod, colresult; valdot = dot - &message[0] + 1; colmod = 0; for (i = 1; i <= msgCount; i++) unmark(i); bufp = buf; mc = 0; np = &namelist[0]; scaninit(); tok = scan(&bufp); star = 0; other = 0; beg = 0; while (tok != TEOL) { switch (tok) { case TNUMBER: number: if (star) { printf("No numbers mixed with *\n"); return (-1); } mc++; other++; if (beg != 0) { if (check(lexnumber, f)) return (-1); for (i = beg; i <= lexnumber; i++) if (f == MDELETED || (message[i - 1].m_flag & MDELETED) == 0) mark(i); beg = 0; break; } beg = lexnumber; if (check(beg, f)) return (-1); tok = scan(&bufp); regret(tok); if (tok != TDASH) { mark(beg); beg = 0; } break; case TPLUS: if (beg != 0) { printf("Non-numeric second argument\n"); return (-1); } i = valdot; do { i++; if (i > msgCount) { printf("Referencing beyond EOF\n"); return (-1); } } while ((message[i - 1].m_flag & MDELETED) != f); mark(i); break; case TDASH: if (beg == 0) { i = valdot; do { i--; if (i <= 0) { printf("Referencing before 1\n"); return (-1); } } while ((message[i - 1].m_flag & MDELETED) != f); mark(i); } break; case TSTRING: if (beg != 0) { printf("Non-numeric second argument\n"); return (-1); } other++; if (lexstring[0] == ':') { colresult = evalcol(lexstring[1]); if (colresult == 0) { printf("Unknown colon modifier \"%s\"\n", lexstring); return (-1); } colmod |= colresult; } else *np++ = savestr(lexstring); break; case TDOLLAR: case TUP: case TDOT: lexnumber = metamess(lexstring[0], f); if (lexnumber == -1) return (-1); goto number; case TSTAR: if (other) { printf("Can't mix \"*\" with anything\n"); return (-1); } star++; break; case TERROR: return (-1); } tok = scan(&bufp); } lastcolmod = colmod; *np = NULL; mc = 0; if (star) { for (i = 0; i < msgCount; i++) if ((message[i].m_flag & MDELETED) == f) { mark(i+1); mc++; } if (mc == 0) { printf("No applicable messages.\n"); return (-1); } return (0); } /* * If no numbers were given, mark all of the messages, * so that we can unmark any whose sender was not selected * if any user names were given. */ if ((np > namelist || colmod != 0) && mc == 0) for (i = 1; i <= msgCount; i++) if ((message[i-1].m_flag & MDELETED) == f) mark(i); /* * If any names were given, go through and eliminate any * messages whose senders were not requested. */ if (np > namelist) { for (i = 1; i <= msgCount; i++) { for (mc = 0, np = &namelist[0]; *np != NULL; np++) if (**np == '/') { if (matchfield(*np, i)) { mc++; break; } } else { if (matchsender(*np, i)) { mc++; break; } } if (mc == 0) unmark(i); } /* * Make sure we got some decent messages. */ mc = 0; for (i = 1; i <= msgCount; i++) if (message[i-1].m_flag & MMARK) { mc++; break; } if (mc == 0) { printf("No applicable messages from {%s", namelist[0]); for (np = &namelist[1]; *np != NULL; np++) printf(", %s", *np); printf("}\n"); return (-1); } } /* * If any colon modifiers were given, go through and * unmark any messages which do not satisfy the modifiers. */ if (colmod != 0) { for (i = 1; i <= msgCount; i++) { struct coltab *colp; mp = &message[i - 1]; for (colp = &coltab[0]; colp->co_char != '\0'; colp++) if (colp->co_bit & colmod) if ((mp->m_flag & colp->co_mask) != colp->co_equal) unmark(i); } for (mp = &message[0]; mp < &message[msgCount]; mp++) if (mp->m_flag & MMARK) break; if (mp >= &message[msgCount]) { struct coltab *colp; printf("No messages satisfy"); for (colp = &coltab[0]; colp->co_char != '\0'; colp++) if (colp->co_bit & colmod) printf(" :%c", colp->co_char); printf("\n"); return (-1); } } return (0); } /* * Turn the character after a colon modifier into a bit * value. */ int evalcol(int col) { struct coltab *colp; if (col == 0) return (lastcolmod); for (colp = &coltab[0]; colp->co_char != '\0'; colp++) if (colp->co_char == col) return (colp->co_bit); return (0); } /* * Check the passed message number for legality and proper flags. * If f is MDELETED, then either kind will do. Otherwise, the message * has to be undeleted. */ int check(int mesg, int f) { struct message *mp; if (mesg < 1 || mesg > msgCount) { printf("%d: Invalid message number\n", mesg); return (-1); } mp = &message[mesg-1]; if (f != MDELETED && (mp->m_flag & MDELETED) != 0) { printf("%d: Inappropriate message\n", mesg); return (-1); } return (0); } /* * Scan out the list of string arguments, shell style * for a RAWLIST. */ int getrawlist(char line[], char **argv, int argc) { char c, *cp, *cp2, quotec; int argn; char *linebuf; size_t linebufsize = BUFSIZ; if ((linebuf = malloc(linebufsize)) == NULL) err(1, "Out of memory"); argn = 0; cp = line; for (;;) { for (; *cp == ' ' || *cp == '\t'; cp++) ; if (*cp == '\0') break; if (argn >= argc - 1) { printf( "Too many elements in the list; excess discarded.\n"); break; } cp2 = linebuf; quotec = '\0'; while ((c = *cp) != '\0') { /* Allocate more space if necessary */ if (cp2 - linebuf == linebufsize - 1) { linebufsize += BUFSIZ; if ((linebuf = realloc(linebuf, linebufsize)) == NULL) err(1, "Out of memory"); cp2 = linebuf + linebufsize - BUFSIZ - 1; } cp++; if (quotec != '\0') { if (c == quotec) quotec = '\0'; else if (c == '\\') switch (c = *cp++) { case '\0': *cp2++ = '\\'; cp--; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c -= '0'; if (*cp >= '0' && *cp <= '7') c = c * 8 + *cp++ - '0'; if (*cp >= '0' && *cp <= '7') c = c * 8 + *cp++ - '0'; *cp2++ = c; break; case 'b': *cp2++ = '\b'; break; case 'f': *cp2++ = '\f'; break; case 'n': *cp2++ = '\n'; break; case 'r': *cp2++ = '\r'; break; case 't': *cp2++ = '\t'; break; case 'v': *cp2++ = '\v'; break; default: *cp2++ = c; } else if (c == '^') { c = *cp++; if (c == '?') *cp2++ = '\177'; /* null doesn't show up anyway */ else if ((c >= 'A' && c <= '_') || (c >= 'a' && c <= 'z')) *cp2++ = c & 037; else { *cp2++ = '^'; cp--; } } else *cp2++ = c; } else if (c == '"' || c == '\'') quotec = c; else if (c == ' ' || c == '\t') break; else *cp2++ = c; } *cp2 = '\0'; argv[argn++] = savestr(linebuf); } argv[argn] = NULL; (void)free(linebuf); return (argn); } /* * scan out a single lexical item and return its token number, * updating the string pointer passed **p. Also, store the value * of the number or string scanned in lexnumber or lexstring as * appropriate. In any event, store the scanned `thing' in lexstring. */ static struct lex { char l_char; char l_token; } singles[] = { { '$', TDOLLAR }, { '.', TDOT }, { '^', TUP }, { '*', TSTAR }, { '-', TDASH }, { '+', TPLUS }, { '(', TOPEN }, { ')', TCLOSE }, { 0, 0 } }; int scan(char **sp) { char *cp, *cp2; int c; struct lex *lp; int quotec; if (regretp >= 0) { strcpy(lexstring, string_stack[regretp]); lexnumber = numberstack[regretp]; return (regretstack[regretp--]); } cp = *sp; cp2 = lexstring; c = *cp++; /* * strip away leading white space. */ while (c == ' ' || c == '\t') c = *cp++; /* * If no characters remain, we are at end of line, * so report that. */ if (c == '\0') { *sp = --cp; return (TEOL); } /* * If the leading character is a digit, scan * the number and convert it on the fly. * Return TNUMBER when done. */ if (isdigit((unsigned char)c)) { lexnumber = 0; while (isdigit((unsigned char)c)) { lexnumber = lexnumber*10 + c - '0'; *cp2++ = c; c = *cp++; } *cp2 = '\0'; *sp = --cp; return (TNUMBER); } /* * Check for single character tokens; return such * if found. */ for (lp = &singles[0]; lp->l_char != '\0'; lp++) if (c == lp->l_char) { lexstring[0] = c; lexstring[1] = '\0'; *sp = cp; return (lp->l_token); } /* * We've got a string! Copy all the characters * of the string into lexstring, until we see * a null, space, or tab. * If the lead character is a " or ', save it * and scan until you get another. */ quotec = 0; if (c == '\'' || c == '"') { quotec = c; c = *cp++; } while (c != '\0') { if (c == quotec) { cp++; break; } if (quotec == 0 && (c == ' ' || c == '\t')) break; if (cp2 - lexstring < STRINGLEN-1) *cp2++ = c; c = *cp++; } if (quotec && c == '\0') { fprintf(stderr, "Missing %c\n", quotec); return (TERROR); } *sp = --cp; *cp2 = '\0'; return (TSTRING); } /* * Unscan the named token by pushing it onto the regret stack. */ void regret(int token) { if (++regretp >= REGDEP) errx(1, "Too many regrets"); regretstack[regretp] = token; lexstring[STRINGLEN-1] = '\0'; string_stack[regretp] = savestr(lexstring); numberstack[regretp] = lexnumber; } /* * Reset all the scanner global variables. */ void scaninit(void) { regretp = -1; } /* * Find the first message whose flags & m == f and return * its message number. */ int first(int f, int m) { struct message *mp; if (msgCount == 0) return (0); f &= MDELETED; m &= MDELETED; for (mp = dot; mp < &message[msgCount]; mp++) if ((mp->m_flag & m) == f) return (mp - message + 1); for (mp = dot-1; mp >= &message[0]; mp--) if ((mp->m_flag & m) == f) return (mp - message + 1); return (0); } /* * See if the passed name sent the passed message number. Return true * if so. */ int matchsender(char *str, int mesg) { char *cp; /* null string matches nothing instead of everything */ if (*str == '\0') return (0); cp = nameof(&message[mesg - 1], 0); return (strcasestr(cp, str) != NULL); } /* * See if the passed name received the passed message number. Return true * if so. */ static char *to_fields[] = { "to", "cc", "bcc", NULL }; static int matchto(char *str, int mesg) { struct message *mp; char *cp, **to; str++; /* null string matches nothing instead of everything */ if (*str == '\0') return (0); mp = &message[mesg - 1]; for (to = to_fields; *to != NULL; to++) { cp = hfield(*to, mp); if (cp != NULL && strcasestr(cp, str) != NULL) return (1); } return (0); } /* * See if the given substring is contained within the specified field. If * 'searchheaders' is set, then the form '/x:y' will be accepted and matches * any message with the substring 'y' in field 'x'. If 'x' is omitted or * 'searchheaders' is not set, then the search matches any messages * with the substring 'y' in the 'Subject'. The search is case insensitive. * * The form '/to:y' is a special case, and will match all messages * containing the substring 'y' in the 'To', 'Cc', or 'Bcc' header * fields. The search for 'to' is case sensitive, so that '/To:y' can * be used to limit the search to just the 'To' field. */ static char lastscan[STRINGLEN]; int matchfield(char *str, int mesg) { struct message *mp; char *cp, *cp2; str++; if (*str == '\0') str = lastscan; else strlcpy(lastscan, str, sizeof(lastscan)); mp = &message[mesg-1]; /* * Now look, ignoring case, for the word in the string. */ if (value("searchheaders") && (cp = strchr(str, ':')) != NULL) { /* Check for special case "/to:" */ if (strncmp(str, "to:", 3) == 0) return (matchto(cp, mesg)); *cp++ = '\0'; cp2 = hfield(*str != '\0' ? str : "subject", mp); cp[-1] = ':'; str = cp; cp = cp2; } else cp = hfield("subject", mp); if (cp == NULL) return (0); return (strcasestr(cp, str) != NULL); } /* * Mark the named message by setting its mark bit. */ void mark(int mesg) { int i; i = mesg; if (i < 1 || i > msgCount) errx(1, "Bad message number to mark"); message[i-1].m_flag |= MMARK; } /* * Unmark the named message. */ void unmark(int mesg) { int i; i = mesg; if (i < 1 || i > msgCount) errx(1, "Bad message number to unmark"); message[i-1].m_flag &= ~MMARK; } /* * Return the message number corresponding to the passed meta character. */ int metamess(int meta, int f) { int c, m; struct message *mp; c = meta; switch (c) { case '^': /* * First 'good' message left. */ for (mp = &message[0]; mp < &message[msgCount]; mp++) if ((mp->m_flag & MDELETED) == f) return (mp - &message[0] + 1); printf("No applicable messages\n"); return (-1); case '$': /* * Last 'good message left. */ for (mp = &message[msgCount-1]; mp >= &message[0]; mp--) if ((mp->m_flag & MDELETED) == f) return (mp - &message[0] + 1); printf("No applicable messages\n"); return (-1); case '.': /* * Current message. */ m = dot - &message[0] + 1; if ((dot->m_flag & MDELETED) != f) { printf("%d: Inappropriate message\n", m); return (-1); } return (m); default: printf("Unknown metachar (%c)\n", c); return (-1); } } diff --git a/usr.bin/mail/names.c b/usr.bin/mail/names.c index 066e2f8058a4..502487055ff9 100644 --- a/usr.bin/mail/names.c +++ b/usr.bin/mail/names.c @@ -1,759 +1,756 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include /* * Mail -- a mail program * * Handle name lists. */ #include "rcv.h" #include #include "extern.h" /* * Allocate a single element of a name list, * initialize its name field to the passed * name and return it. */ struct name * nalloc(char str[], int ntype) { struct name *np; np = (struct name *)salloc(sizeof(*np)); np->n_flink = NULL; np->n_blink = NULL; np->n_type = ntype; np->n_name = savestr(str); return (np); } /* * Find the tail of a list and return it. */ struct name * tailof(struct name *name) { struct name *np; np = name; if (np == NULL) return (NULL); while (np->n_flink != NULL) np = np->n_flink; return (np); } /* * Extract a list of names from a line, * and make a list of names from it. * Return the list or NULL if none found. */ struct name * extract(char *line, int ntype) { char *cp, *nbuf; struct name *top, *np, *t; if (line == NULL || *line == '\0') return (NULL); if ((nbuf = malloc(strlen(line) + 1)) == NULL) err(1, "Out of memory"); top = NULL; np = NULL; cp = line; while ((cp = yankword(cp, nbuf)) != NULL) { t = nalloc(nbuf, ntype); if (top == NULL) top = t; else np->n_flink = t; t->n_blink = np; np = t; } (void)free(nbuf); return (top); } /* * Turn a list of names into a string of the same names. */ char * detract(struct name *np, int ntype) { int s, comma; char *cp, *top; struct name *p; comma = ntype & GCOMMA; if (np == NULL) return (NULL); ntype &= ~GCOMMA; s = 0; if (debug && comma) fprintf(stderr, "detract asked to insert commas\n"); for (p = np; p != NULL; p = p->n_flink) { if (ntype && (p->n_type & GMASK) != ntype) continue; s += strlen(p->n_name) + 1; if (comma) s++; } if (s == 0) return (NULL); s += 2; top = salloc(s); cp = top; for (p = np; p != NULL; p = p->n_flink) { if (ntype && (p->n_type & GMASK) != ntype) continue; cp += strlcpy(cp, p->n_name, strlen(p->n_name) + 1); if (comma && p->n_flink != NULL) *cp++ = ','; *cp++ = ' '; } *--cp = '\0'; if (comma && *--cp == ',') *cp = '\0'; return (top); } /* * Grab a single word (liberal word) * Throw away things between ()'s, and take anything between <>. */ char * yankword(char *ap, char *wbuf) { char *cp, *cp2; cp = ap; for (;;) { if (*cp == '\0') return (NULL); if (*cp == '(') { int nesting = 0; while (*cp != '\0') { switch (*cp++) { case '(': nesting++; break; case ')': --nesting; break; } if (nesting <= 0) break; } } else if (*cp == ' ' || *cp == '\t' || *cp == ',') cp++; else break; } if (*cp == '<') for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';) ; else for (cp2 = wbuf; *cp != '\0' && strchr(" \t,(", *cp) == NULL; *cp2++ = *cp++) ; *cp2 = '\0'; return (cp); } /* * Grab a single login name (liberal word) * Throw away things between ()'s, take anything between <>, * and look for words before metacharacters %, @, !. */ char * yanklogin(char *ap, char *wbuf) { char *cp, *cp2, *cp_temp; int n; cp = ap; for (;;) { if (*cp == '\0') return (NULL); if (*cp == '(') { int nesting = 0; while (*cp != '\0') { switch (*cp++) { case '(': nesting++; break; case ')': --nesting; break; } if (nesting <= 0) break; } } else if (*cp == ' ' || *cp == '\t' || *cp == ',') cp++; else break; } /* * Now, let's go forward till we meet the needed character, * and step one word back. */ /* First, remember current point. */ cp_temp = cp; n = 0; /* * Note that we look ahead in a cycle. This is safe, since * non-end of string is checked first. */ while(*cp != '\0' && strchr("@%!", *(cp + 1)) == NULL) cp++; /* * Now, start stepping back to the first non-word character, * while counting the number of symbols in a word. */ while(cp != cp_temp && strchr(" \t,<>", *(cp - 1)) == NULL) { n++; cp--; } /* Finally, grab the word forward. */ cp2 = wbuf; while(n >= 0) { *cp2++=*cp++; n--; } *cp2 = '\0'; return (cp); } /* * For each recipient in the passed name list with a / * in the name, append the message to the end of the named file * and remove him from the recipient list. * * Recipients whose name begins with | are piped through the given * program and removed. */ struct name * outof(struct name *names, FILE *fo, struct header *hp) { int c, ispipe; struct name *np, *top; time_t now; char *date, *fname; FILE *fout, *fin; top = names; np = names; (void)time(&now); date = ctime(&now); while (np != NULL) { if (!isfileaddr(np->n_name) && np->n_name[0] != '|') { np = np->n_flink; continue; } ispipe = np->n_name[0] == '|'; if (ispipe) fname = np->n_name+1; else fname = expand(np->n_name); /* * See if we have copied the complete message out yet. * If not, do so. */ if (image < 0) { int fd; char tempname[PATHSIZE]; (void)snprintf(tempname, sizeof(tempname), "%s/mail.ReXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (fout = Fdopen(fd, "a")) == NULL) { warn("%s", tempname); senderr++; goto cant; } image = open(tempname, O_RDWR); (void)rm(tempname); if (image < 0) { warn("%s", tempname); senderr++; (void)Fclose(fout); goto cant; } (void)fcntl(image, F_SETFD, 1); fprintf(fout, "From %s %s", myname, date); puthead(hp, fout, GTO|GSUBJECT|GCC|GREPLYTO|GINREPLYTO|GNL); while ((c = getc(fo)) != EOF) (void)putc(c, fout); rewind(fo); fprintf(fout, "\n"); (void)fflush(fout); if (ferror(fout)) { warn("%s", tempname); senderr++; (void)Fclose(fout); goto cant; } (void)Fclose(fout); } /* * Now either copy "image" to the desired file * or give it as the standard input to the desired * program as appropriate. */ if (ispipe) { int pid; char *sh; sigset_t nset; /* * XXX * We can't really reuse the same image file, * because multiple piped recipients will * share the same lseek location and trample * on one another. */ if ((sh = value("SHELL")) == NULL) sh = _PATH_CSHELL; (void)sigemptyset(&nset); (void)sigaddset(&nset, SIGHUP); (void)sigaddset(&nset, SIGINT); (void)sigaddset(&nset, SIGQUIT); pid = start_command(sh, &nset, image, -1, "-c", fname, NULL); if (pid < 0) { senderr++; goto cant; } free_child(pid); } else { int f; if ((fout = Fopen(fname, "a")) == NULL) { warn("%s", fname); senderr++; goto cant; } if ((f = dup(image)) < 0) { warn("dup"); fin = NULL; } else fin = Fdopen(f, "r"); if (fin == NULL) { fprintf(stderr, "Can't reopen image\n"); (void)Fclose(fout); senderr++; goto cant; } rewind(fin); while ((c = getc(fin)) != EOF) (void)putc(c, fout); if (ferror(fout)) { warnx("%s", fname); senderr++; (void)Fclose(fout); (void)Fclose(fin); goto cant; } (void)Fclose(fout); (void)Fclose(fin); } cant: /* * In days of old we removed the entry from the * the list; now for sake of header expansion * we leave it in and mark it as deleted. */ np->n_type |= GDEL; np = np->n_flink; } if (image >= 0) { (void)close(image); image = -1; } return (top); } /* * Determine if the passed address is a local "send to file" address. * If any of the network metacharacters precedes any slashes, it can't * be a filename. We cheat with .'s to allow path names like ./... */ int isfileaddr(char *name) { char *cp; if (*name == '+') return (1); for (cp = name; *cp != '\0'; cp++) { if (*cp == '!' || *cp == '%' || *cp == '@') return (0); if (*cp == '/') return (1); } return (0); } /* * Map all of the aliased users in the invoker's mailrc * file and insert them into the list. * Changed after all these months of service to recursively * expand names (2/14/80). */ struct name * usermap(struct name *names) { struct name *new, *np, *cp; struct grouphead *gh; int metoo; new = NULL; np = names; metoo = (value("metoo") != NULL); while (np != NULL) { if (np->n_name[0] == '\\') { cp = np->n_flink; new = put(new, np); np = cp; continue; } gh = findgroup(np->n_name); cp = np->n_flink; if (gh != NULL) new = gexpand(new, gh, metoo, np->n_type); else new = put(new, np); np = cp; } return (new); } /* * Recursively expand a group name. We limit the expansion to some * fixed level to keep things from going haywire. * Direct recursion is not expanded for convenience. */ struct name * gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype) { struct group *gp; struct grouphead *ngh; struct name *np; static int depth; char *cp; if (depth > MAXEXP) { printf("Expanding alias to depth larger than %d\n", MAXEXP); return (nlist); } depth++; for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) { cp = gp->ge_name; if (*cp == '\\') goto quote; if (strcmp(cp, gh->g_name) == 0) goto quote; if ((ngh = findgroup(cp)) != NULL) { nlist = gexpand(nlist, ngh, metoo, ntype); continue; } quote: np = nalloc(cp, ntype); /* * At this point should allow to expand * to self if only person in group */ if (gp == gh->g_list && gp->ge_link == NULL) goto skip; if (!metoo && strcmp(cp, myname) == 0) np->n_type |= GDEL; skip: nlist = put(nlist, np); } depth--; return (nlist); } /* * Concatenate the two passed name lists, return the result. */ struct name * cat(struct name *n1, struct name *n2) { struct name *tail; if (n1 == NULL) return (n2); if (n2 == NULL) return (n1); tail = tailof(n1); tail->n_flink = n2; n2->n_blink = tail; return (n1); } /* * Unpack the name list onto a vector of strings. * Return an error if the name list won't fit. */ char ** unpack(struct name *np) { char **ap, **top; struct name *n; int t, extra, metoo, verbose; n = np; if ((t = count(n)) == 0) errx(1, "No names to unpack"); /* * Compute the number of extra arguments we will need. * We need at least two extra -- one for "mail" and one for * the terminating 0 pointer. Additional spots may be needed * to pass along -f to the host mailer. */ extra = 2; extra++; metoo = value("metoo") != NULL; if (metoo) extra++; verbose = value("verbose") != NULL; if (verbose) extra++; top = (char **)salloc((t + extra) * sizeof(*top)); ap = top; *ap++ = "sendmail"; *ap++ = "-i"; if (metoo) *ap++ = "-m"; if (verbose) *ap++ = "-v"; for (; n != NULL; n = n->n_flink) if ((n->n_type & GDEL) == 0) *ap++ = n->n_name; *ap = NULL; return (top); } /* * Remove all of the duplicates from the passed name list by * insertion sorting them, then checking for dups. * Return the head of the new list. */ struct name * elide(struct name *names) { struct name *np, *t, *new; struct name *x; if (names == NULL) return (NULL); new = names; np = names; np = np->n_flink; if (np != NULL) np->n_blink = NULL; new->n_flink = NULL; while (np != NULL) { t = new; while (strcasecmp(t->n_name, np->n_name) < 0) { if (t->n_flink == NULL) break; t = t->n_flink; } /* * If we ran out of t's, put the new entry after * the current value of t. */ if (strcasecmp(t->n_name, np->n_name) < 0) { t->n_flink = np; np->n_blink = t; t = np; np = np->n_flink; t->n_flink = NULL; continue; } /* * Otherwise, put the new entry in front of the * current t. If at the front of the list, * the new guy becomes the new head of the list. */ if (t == new) { t = np; np = np->n_flink; t->n_flink = new; new->n_blink = t; t->n_blink = NULL; new = t; continue; } /* * The normal case -- we are inserting into the * middle of the list. */ x = np; np = np->n_flink; x->n_flink = t; x->n_blink = t->n_blink; t->n_blink->n_flink = x; t->n_blink = x; } /* * Now the list headed up by new is sorted. * Go through it and remove duplicates. */ np = new; while (np != NULL) { t = np; while (t->n_flink != NULL && strcasecmp(np->n_name, t->n_flink->n_name) == 0) t = t->n_flink; if (t == np || t == NULL) { np = np->n_flink; continue; } /* * Now t points to the last entry with the same name * as np. Make np point beyond t. */ np->n_flink = t->n_flink; if (t->n_flink != NULL) t->n_flink->n_blink = np; np = np->n_flink; } return (new); } /* * Put another node onto a list of names and return * the list. */ struct name * put(struct name *list, struct name *node) { node->n_flink = list; node->n_blink = NULL; if (list != NULL) list->n_blink = node; return (node); } /* * Determine the number of undeleted elements in * a name list and return it. */ int count(struct name *np) { int c; for (c = 0; np != NULL; np = np->n_flink) if ((np->n_type & GDEL) == 0) c++; return (c); } /* * Delete the given name from a namelist. */ struct name * delname(struct name *np, char name[]) { struct name *p; for (p = np; p != NULL; p = p->n_flink) if (strcasecmp(p->n_name, name) == 0) { if (p->n_blink == NULL) { if (p->n_flink != NULL) p->n_flink->n_blink = NULL; np = p->n_flink; continue; } if (p->n_flink == NULL) { if (p->n_blink != NULL) p->n_blink->n_flink = NULL; continue; } p->n_blink->n_flink = p->n_flink; p->n_flink->n_blink = p->n_blink; } return (np); } /* * Pretty print a name list * Uncomment it if you need it. */ /* void prettyprint(struct name *name) { struct name *np; np = name; while (np != NULL) { fprintf(stderr, "%s(%d) ", np->n_name, np->n_type); np = np->n_flink; } fprintf(stderr, "\n"); } */ diff --git a/usr.bin/mail/popen.c b/usr.bin/mail/popen.c index 3dee3b9c162f..69326bdb260c 100644 --- a/usr.bin/mail/popen.c +++ b/usr.bin/mail/popen.c @@ -1,409 +1,406 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include #include #include #include #include "extern.h" #define READ 0 #define WRITE 1 struct fp { FILE *fp; int pipe; pid_t pid; struct fp *link; }; static struct fp *fp_head; struct child { pid_t pid; char done; char free; int status; struct child *link; }; static struct child *child, *child_freelist = NULL; static void delchild(struct child *); static pid_t file_pid(FILE *); static pid_t start_commandv(char *, sigset_t *, int, int, va_list); FILE * Fopen(const char *path, const char *mode) { FILE *fp; if ((fp = fopen(path, mode)) != NULL) { register_file(fp, 0, 0); (void)fcntl(fileno(fp), F_SETFD, 1); } return (fp); } FILE * Fdopen(int fd, const char *mode) { FILE *fp; if ((fp = fdopen(fd, mode)) != NULL) { register_file(fp, 0, 0); (void)fcntl(fileno(fp), F_SETFD, 1); } return (fp); } int Fclose(FILE *fp) { unregister_file(fp); return (fclose(fp)); } FILE * Popen(char *cmd, const char *mode) { int p[2]; int myside, hisside, fd0, fd1; pid_t pid; sigset_t nset; FILE *fp; if (pipe(p) < 0) return (NULL); (void)fcntl(p[READ], F_SETFD, 1); (void)fcntl(p[WRITE], F_SETFD, 1); if (*mode == 'r') { myside = p[READ]; hisside = fd0 = fd1 = p[WRITE]; } else { myside = p[WRITE]; hisside = fd0 = p[READ]; fd1 = -1; } (void)sigemptyset(&nset); pid = start_command(value("SHELL"), &nset, fd0, fd1, "-c", cmd, NULL); if (pid < 0) { (void)close(p[READ]); (void)close(p[WRITE]); return (NULL); } (void)close(hisside); if ((fp = fdopen(myside, mode)) != NULL) register_file(fp, 1, pid); return (fp); } int Pclose(FILE *ptr) { int i; sigset_t nset, oset; i = file_pid(ptr); unregister_file(ptr); (void)fclose(ptr); (void)sigemptyset(&nset); (void)sigaddset(&nset, SIGINT); (void)sigaddset(&nset, SIGHUP); (void)sigprocmask(SIG_BLOCK, &nset, &oset); i = wait_child(i); (void)sigprocmask(SIG_SETMASK, &oset, NULL); return (i); } void close_all_files(void) { while (fp_head != NULL) if (fp_head->pipe) (void)Pclose(fp_head->fp); else (void)Fclose(fp_head->fp); } void register_file(FILE *fp, int pipe, pid_t pid) { struct fp *fpp; if ((fpp = malloc(sizeof(*fpp))) == NULL) err(1, "Out of memory"); fpp->fp = fp; fpp->pipe = pipe; fpp->pid = pid; fpp->link = fp_head; fp_head = fpp; } void unregister_file(FILE *fp) { struct fp **pp, *p; for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link) if (p->fp == fp) { *pp = p->link; (void)free(p); return; } errx(1, "Invalid file pointer"); /*NOTREACHED*/ } pid_t file_pid(FILE *fp) { struct fp *p; for (p = fp_head; p != NULL; p = p->link) if (p->fp == fp) return (p->pid); errx(1, "Invalid file pointer"); /*NOTREACHED*/ } /* * Run a command without a shell, with optional arguments and splicing * of stdin (-1 means none) and stdout. The command name can be a sequence * of words. * Signals must be handled by the caller. * "nset" contains the signals to ignore in the new process. * SIGINT is enabled unless it's in "nset". */ static pid_t start_commandv(char *cmd, sigset_t *nset, int infd, int outfd, va_list args) { pid_t pid; if ((pid = fork()) < 0) { warn("fork"); return (-1); } if (pid == 0) { char *argv[100]; int i = getrawlist(cmd, argv, sizeof(argv) / sizeof(*argv)); while ((argv[i++] = va_arg(args, char *))) ; argv[i] = NULL; prepare_child(nset, infd, outfd); execvp(argv[0], argv); warn("%s", argv[0]); _exit(1); } return (pid); } int run_command(char *cmd, sigset_t *nset, int infd, int outfd, ...) { pid_t pid; va_list args; va_start(args, outfd); pid = start_commandv(cmd, nset, infd, outfd, args); va_end(args); if (pid < 0) return -1; return wait_command(pid); } int start_command(char *cmd, sigset_t *nset, int infd, int outfd, ...) { va_list args; int r; va_start(args, outfd); r = start_commandv(cmd, nset, infd, outfd, args); va_end(args); return r; } void prepare_child(sigset_t *nset, int infd, int outfd) { int i; sigset_t eset; /* * All file descriptors other than 0, 1, and 2 are supposed to be * close-on-exec. */ if (infd >= 0) dup2(infd, 0); if (outfd >= 0) dup2(outfd, 1); for (i = 1; i < NSIG; i++) if (nset != NULL && sigismember(nset, i)) (void)signal(i, SIG_IGN); if (nset == NULL || !sigismember(nset, SIGINT)) (void)signal(SIGINT, SIG_DFL); (void)sigemptyset(&eset); (void)sigprocmask(SIG_SETMASK, &eset, NULL); } int wait_command(pid_t pid) { if (wait_child(pid) < 0) { printf("Fatal error in process.\n"); return (-1); } return (0); } static struct child * findchild(pid_t pid, int dont_alloc) { struct child **cpp; for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid; cpp = &(*cpp)->link) ; if (*cpp == NULL) { if (dont_alloc) return(NULL); if (child_freelist) { *cpp = child_freelist; child_freelist = (*cpp)->link; } else { *cpp = malloc(sizeof(struct child)); if (*cpp == NULL) err(1, "malloc"); } (*cpp)->pid = pid; (*cpp)->done = (*cpp)->free = 0; (*cpp)->link = NULL; } return (*cpp); } static void delchild(struct child *cp) { struct child **cpp; for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link) ; *cpp = cp->link; cp->link = child_freelist; child_freelist = cp; } /*ARGSUSED*/ void sigchild(int signo __unused) { pid_t pid; int status; struct child *cp; int save_errno; save_errno = errno; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { cp = findchild(pid, 1); if (cp == NULL) continue; if (cp->free) delchild(cp); else { cp->done = 1; cp->status = status; } } errno = save_errno; } int wait_status; /* * Wait for a specific child to die. */ int wait_child(pid_t pid) { struct child *cp; sigset_t nset, oset; pid_t rv = 0; (void)sigemptyset(&nset); (void)sigaddset(&nset, SIGCHLD); (void)sigprocmask(SIG_BLOCK, &nset, &oset); /* * If we have not already waited on the pid (via sigchild) * wait on it now. Otherwise, use the wait status stashed * by sigchild. */ cp = findchild(pid, 1); if (cp == NULL || !cp->done) rv = waitpid(pid, &wait_status, 0); else wait_status = cp->status; if (cp != NULL) delchild(cp); (void)sigprocmask(SIG_SETMASK, &oset, NULL); if (rv == -1 || (WIFEXITED(wait_status) && WEXITSTATUS(wait_status))) return -1; else return 0; } /* * Mark a child as don't care. */ void free_child(pid_t pid) { struct child *cp; sigset_t nset, oset; (void)sigemptyset(&nset); (void)sigaddset(&nset, SIGCHLD); (void)sigprocmask(SIG_BLOCK, &nset, &oset); if ((cp = findchild(pid, 0)) != NULL) { if (cp->done) delchild(cp); else cp->free = 1; } (void)sigprocmask(SIG_SETMASK, &oset, NULL); } diff --git a/usr.bin/mail/quit.c b/usr.bin/mail/quit.c index 447c59a78bde..6b69461ca6f4 100644 --- a/usr.bin/mail/quit.c +++ b/usr.bin/mail/quit.c @@ -1,492 +1,489 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include #include "extern.h" /* * Rcv -- receive mail rationally. * * Termination processing. */ /* * The "quit" command. */ int quitcmd(void *arg __unused) { /* * If we are sourcing, then return 1 so execute() can handle it. * Otherwise, return -1 to abort command loop. */ if (sourcing) return (1); return (-1); } /* * Save all of the undetermined messages at the top of "mbox" * Save all untouched messages back in the system mailbox. * Remove the system mailbox, if none saved there. */ void quit(void) { int mcount, p, modify, autohold, anystat, holdbit, nohold; FILE *ibuf, *obuf, *fbuf, *rbuf, *readstat, *abuf; struct message *mp; int c, fd; struct stat minfo; char *mbox, tempname[PATHSIZE]; /* * If we are read only, we can't do anything, * so just return quickly. */ if (readonly) return; /* * If editing (not reading system mail box), then do the work * in edstop() */ if (edit) { edstop(); return; } /* * See if there any messages to save in mbox. If no, we * can save copying mbox to /tmp and back. * * Check also to see if any files need to be preserved. * Delete all untouched messages to keep them out of mbox. * If all the messages are to be preserved, just exit with * a message. */ fbuf = Fopen(mailname, "r"); if (fbuf == NULL) goto newmail; (void)flock(fileno(fbuf), LOCK_EX); rbuf = NULL; if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) { printf("New mail has arrived.\n"); (void)snprintf(tempname, sizeof(tempname), "%s/mail.RqXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (rbuf = Fdopen(fd, "w")) == NULL) goto newmail; #ifdef APPEND (void)fseeko(fbuf, mailsize, SEEK_SET); while ((c = getc(fbuf)) != EOF) (void)putc(c, rbuf); #else p = minfo.st_size - mailsize; while (p-- > 0) { c = getc(fbuf); if (c == EOF) goto newmail; (void)putc(c, rbuf); } #endif (void)Fclose(rbuf); if ((rbuf = Fopen(tempname, "r")) == NULL) goto newmail; (void)rm(tempname); } /* * Adjust the message flags in each message. */ anystat = 0; autohold = value("hold") != NULL; holdbit = autohold ? MPRESERVE : MBOX; nohold = MBOX|MSAVED|MDELETED|MPRESERVE; if (value("keepsave") != NULL) nohold &= ~MSAVED; for (mp = &message[0]; mp < &message[msgCount]; mp++) { if (mp->m_flag & MNEW) { mp->m_flag &= ~MNEW; mp->m_flag |= MSTATUS; } if (mp->m_flag & MSTATUS) anystat++; if ((mp->m_flag & MTOUCH) == 0) mp->m_flag |= MPRESERVE; if ((mp->m_flag & nohold) == 0) mp->m_flag |= holdbit; } modify = 0; if (Tflag != NULL) { if ((readstat = Fopen(Tflag, "w")) == NULL) Tflag = NULL; } for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) { if (mp->m_flag & MBOX) c++; if (mp->m_flag & MPRESERVE) p++; if (mp->m_flag & MODIFY) modify++; if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) { char *id; if ((id = hfield("article-id", mp)) != NULL) fprintf(readstat, "%s\n", id); } } if (Tflag != NULL) (void)Fclose(readstat); if (p == msgCount && !modify && !anystat) { printf("Held %d message%s in %s\n", p, p == 1 ? "" : "s", mailname); (void)Fclose(fbuf); return; } if (c == 0) { if (p != 0) { writeback(rbuf); (void)Fclose(fbuf); return; } goto cream; } /* * Create another temporary file and copy user's mbox file * darin. If there is no mbox, copy nothing. * If he has specified "append" don't copy his mailbox, * just copy saveable entries at the end. */ mbox = expand("&"); mcount = c; if (value("append") == NULL) { (void)snprintf(tempname, sizeof(tempname), "%s/mail.RmXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (obuf = Fdopen(fd, "w")) == NULL) { warn("%s", tempname); (void)Fclose(fbuf); return; } if ((ibuf = Fopen(tempname, "r")) == NULL) { warn("%s", tempname); (void)rm(tempname); (void)Fclose(obuf); (void)Fclose(fbuf); return; } (void)rm(tempname); if ((abuf = Fopen(mbox, "r")) != NULL) { while ((c = getc(abuf)) != EOF) (void)putc(c, obuf); (void)Fclose(abuf); } if (ferror(obuf)) { warnx("%s", tempname); (void)Fclose(ibuf); (void)Fclose(obuf); (void)Fclose(fbuf); return; } (void)Fclose(obuf); if ((fd = open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600)) >= 0) (void)close(fd); if ((obuf = Fopen(mbox, "r+")) == NULL) { warn("%s", mbox); (void)Fclose(ibuf); (void)Fclose(fbuf); return; } } if (value("append") != NULL) { if ((obuf = Fopen(mbox, "a")) == NULL) { warn("%s", mbox); (void)Fclose(fbuf); return; } (void)fchmod(fileno(obuf), 0600); } for (mp = &message[0]; mp < &message[msgCount]; mp++) if (mp->m_flag & MBOX) if (sendmessage(mp, obuf, saveignore, NULL) < 0) { warnx("%s", mbox); (void)Fclose(ibuf); (void)Fclose(obuf); (void)Fclose(fbuf); return; } /* * Copy the user's old mbox contents back * to the end of the stuff we just saved. * If we are appending, this is unnecessary. */ if (value("append") == NULL) { rewind(ibuf); c = getc(ibuf); while (c != EOF) { (void)putc(c, obuf); if (ferror(obuf)) break; c = getc(ibuf); } (void)Fclose(ibuf); } (void)fflush(obuf); trunc(obuf); if (ferror(obuf)) { warn("%s", mbox); (void)Fclose(obuf); (void)Fclose(fbuf); return; } (void)Fclose(obuf); if (mcount == 1) printf("Saved 1 message in mbox\n"); else printf("Saved %d messages in mbox\n", mcount); /* * Now we are ready to copy back preserved files to * the system mailbox, if any were requested. */ if (p != 0) { writeback(rbuf); (void)Fclose(fbuf); return; } /* * Finally, remove his /var/mail file. * If new mail has arrived, copy it back. */ cream: if (rbuf != NULL) { abuf = Fopen(mailname, "r+"); if (abuf == NULL) goto newmail; while ((c = getc(rbuf)) != EOF) (void)putc(c, abuf); (void)Fclose(rbuf); trunc(abuf); (void)Fclose(abuf); alter(mailname); (void)Fclose(fbuf); return; } demail(); (void)Fclose(fbuf); return; newmail: printf("Thou hast new mail.\n"); if (fbuf != NULL) (void)Fclose(fbuf); } /* * Preserve all the appropriate messages back in the system * mailbox, and print a nice message indicated how many were * saved. On any error, just return -1. Else return 0. * Incorporate the any new mail that we found. */ int writeback(FILE *res) { struct message *mp; int p, c; FILE *obuf; p = 0; if ((obuf = Fopen(mailname, "r+")) == NULL) { warn("%s", mailname); return (-1); } #ifndef APPEND if (res != NULL) while ((c = getc(res)) != EOF) (void)putc(c, obuf); #endif for (mp = &message[0]; mp < &message[msgCount]; mp++) if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) { p++; if (sendmessage(mp, obuf, NULL, NULL) < 0) { warnx("%s", mailname); (void)Fclose(obuf); return (-1); } } #ifdef APPEND if (res != NULL) while ((c = getc(res)) != EOF) (void)putc(c, obuf); #endif (void)fflush(obuf); trunc(obuf); if (ferror(obuf)) { warn("%s", mailname); (void)Fclose(obuf); return (-1); } if (res != NULL) (void)Fclose(res); (void)Fclose(obuf); alter(mailname); if (p == 1) printf("Held 1 message in %s\n", mailname); else printf("Held %d messages in %s\n", p, mailname); return (0); } /* * Terminate an editing session by attempting to write out the user's * file from the temporary. Save any new stuff appended to the file. */ void edstop(void) { int gotcha, c; struct message *mp; FILE *obuf, *ibuf, *readstat; struct stat statb; char tempname[PATHSIZE]; if (readonly) return; holdsigs(); if (Tflag != NULL) { if ((readstat = Fopen(Tflag, "w")) == NULL) Tflag = NULL; } for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) { if (mp->m_flag & MNEW) { mp->m_flag &= ~MNEW; mp->m_flag |= MSTATUS; } if (mp->m_flag & (MODIFY|MDELETED|MSTATUS)) gotcha++; if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) { char *id; if ((id = hfield("article-id", mp)) != NULL) fprintf(readstat, "%s\n", id); } } if (Tflag != NULL) (void)Fclose(readstat); if (!gotcha || Tflag != NULL) goto done; ibuf = NULL; if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) { int fd; (void)snprintf(tempname, sizeof(tempname), "%s/mbox.XXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (obuf = Fdopen(fd, "w")) == NULL) { warn("%s", tempname); relsesigs(); reset(0); } if ((ibuf = Fopen(mailname, "r")) == NULL) { warn("%s", mailname); (void)Fclose(obuf); (void)rm(tempname); relsesigs(); reset(0); } (void)fseeko(ibuf, mailsize, SEEK_SET); while ((c = getc(ibuf)) != EOF) (void)putc(c, obuf); (void)Fclose(ibuf); (void)Fclose(obuf); if ((ibuf = Fopen(tempname, "r")) == NULL) { warn("%s", tempname); (void)rm(tempname); relsesigs(); reset(0); } (void)rm(tempname); } printf("\"%s\" ", mailname); (void)fflush(stdout); if ((obuf = Fopen(mailname, "r+")) == NULL) { warn("%s", mailname); relsesigs(); reset(0); } trunc(obuf); c = 0; for (mp = &message[0]; mp < &message[msgCount]; mp++) { if ((mp->m_flag & MDELETED) != 0) continue; c++; if (sendmessage(mp, obuf, NULL, NULL) < 0) { warnx("%s", mailname); relsesigs(); reset(0); } } gotcha = (c == 0 && ibuf == NULL); if (ibuf != NULL) { while ((c = getc(ibuf)) != EOF) (void)putc(c, obuf); (void)Fclose(ibuf); } (void)fflush(obuf); if (ferror(obuf)) { warn("%s", mailname); relsesigs(); reset(0); } (void)Fclose(obuf); if (gotcha) { (void)rm(mailname); printf("removed\n"); } else printf("complete\n"); (void)fflush(stdout); done: relsesigs(); } diff --git a/usr.bin/mail/send.c b/usr.bin/mail/send.c index be1582351539..b4fbd4cbfd8a 100644 --- a/usr.bin/mail/send.c +++ b/usr.bin/mail/send.c @@ -1,587 +1,584 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include "extern.h" /* * Mail -- a mail program * * Mail to others. */ /* * Send message described by the passed pointer to the * passed output buffer. Return -1 on error. * Adjust the status: field if need be. * If doign is given, suppress ignored header fields. * prefix is a string to prepend to each output line. */ int sendmessage(struct message *mp, FILE *obuf, struct ignoretab *doign, char *prefix) { long count; FILE *ibuf; char *cp, *cp2, line[LINESIZE]; int ishead, infld, ignoring, dostat, firstline; int c = 0, length, prefixlen; /* * Compute the prefix string, without trailing whitespace */ if (prefix != NULL) { cp2 = 0; for (cp = prefix; *cp != '\0'; cp++) if (*cp != ' ' && *cp != '\t') cp2 = cp; prefixlen = cp2 == NULL ? 0 : cp2 - prefix + 1; } ibuf = setinput(mp); count = mp->m_size; ishead = 1; dostat = doign == 0 || !isign("status", doign); infld = 0; firstline = 1; /* * Process headers first */ while (count > 0 && ishead) { if (fgets(line, sizeof(line), ibuf) == NULL) break; count -= length = strlen(line); if (firstline) { /* * First line is the From line, so no headers * there to worry about */ firstline = 0; ignoring = doign == ignoreall; } else if (line[0] == '\n') { /* * If line is blank, we've reached end of * headers, so force out status: field * and note that we are no longer in header * fields */ if (dostat) { statusput(mp, obuf, prefix); dostat = 0; } ishead = 0; ignoring = doign == ignoreall; } else if (infld && (line[0] == ' ' || line[0] == '\t')) { /* * If this line is a continuation (via space or tab) * of a previous header field, just echo it * (unless the field should be ignored). * In other words, nothing to do. */ } else { /* * Pick up the header field if we have one. */ for (cp = line; (c = *cp++) != '\0' && c != ':' && !isspace((unsigned char)c);) ; cp2 = --cp; while (isspace((unsigned char)*cp++)) ; if (cp[-1] != ':') { /* * Not a header line, force out status: * This happens in uucp style mail where * there are no headers at all. */ if (dostat) { statusput(mp, obuf, prefix); dostat = 0; } if (doign != ignoreall) /* add blank line */ (void)putc('\n', obuf); ishead = 0; ignoring = 0; } else { /* * If it is an ignored field and * we care about such things, skip it. */ *cp2 = '\0'; /* temporarily null terminate */ if (doign && isign(line, doign)) ignoring = 1; else if ((line[0] == 's' || line[0] == 'S') && strcasecmp(line, "status") == 0) { /* * If the field is "status," go compute * and print the real Status: field */ if (dostat) { statusput(mp, obuf, prefix); dostat = 0; } ignoring = 1; } else { ignoring = 0; *cp2 = c; /* restore */ } infld = 1; } } if (!ignoring) { /* * Strip trailing whitespace from prefix * if line is blank. */ if (prefix != NULL) { if (length > 1) fputs(prefix, obuf); else (void)fwrite(prefix, sizeof(*prefix), prefixlen, obuf); } (void)fwrite(line, sizeof(*line), length, obuf); if (ferror(obuf)) return (-1); } } /* * Copy out message body */ if (doign == ignoreall) count--; /* skip final blank line */ if (prefix != NULL) while (count > 0) { if (fgets(line, sizeof(line), ibuf) == NULL) { c = 0; break; } count -= c = strlen(line); /* * Strip trailing whitespace from prefix * if line is blank. */ if (c > 1) fputs(prefix, obuf); else (void)fwrite(prefix, sizeof(*prefix), prefixlen, obuf); (void)fwrite(line, sizeof(*line), c, obuf); if (ferror(obuf)) return (-1); } else while (count > 0) { c = count < LINESIZE ? count : LINESIZE; if ((c = fread(line, sizeof(*line), c, ibuf)) <= 0) break; count -= c; if (fwrite(line, sizeof(*line), c, obuf) != c) return (-1); } if (doign == ignoreall && c > 0 && line[c - 1] != '\n') /* no final blank line */ if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF) return (-1); return (0); } /* * Output a reasonable looking status field. */ void statusput(struct message *mp, FILE *obuf, char *prefix) { char statout[3]; char *cp = statout; if (mp->m_flag & MREAD) *cp++ = 'R'; if ((mp->m_flag & MNEW) == 0) *cp++ = 'O'; *cp = '\0'; if (statout[0] != '\0') fprintf(obuf, "%sStatus: %s\n", prefix == NULL ? "" : prefix, statout); } /* * Interface between the argument list and the mail1 routine * which does all the dirty work. */ int mail(struct name *to, struct name *cc, struct name *bcc, struct name *smopts, char *subject, char *replyto) { struct header head; head.h_to = to; head.h_subject = subject; head.h_cc = cc; head.h_bcc = bcc; head.h_smopts = smopts; head.h_replyto = replyto; head.h_inreplyto = NULL; mail1(&head, 0); return (0); } /* * Send mail to a bunch of user names. The interface is through * the mail routine below. */ int sendmail(void *str) { struct header head; head.h_to = extract(str, GTO); head.h_subject = NULL; head.h_cc = NULL; head.h_bcc = NULL; head.h_smopts = NULL; head.h_replyto = value("REPLYTO"); head.h_inreplyto = NULL; mail1(&head, 0); return (0); } /* * Mail a message on standard input to the people indicated * in the passed header. (Internal interface). */ void mail1(struct header *hp, int printheaders) { char *cp; char *nbuf; int pid; char **namelist; struct name *to, *nsto; FILE *mtf; /* * Collect user's mail from standard input. * Get the result as mtf. */ if ((mtf = collect(hp, printheaders)) == NULL) return; if (value("interactive") != NULL) { if (value("askcc") != NULL || value("askbcc") != NULL) { if (value("askcc") != NULL) grabh(hp, GCC); if (value("askbcc") != NULL) grabh(hp, GBCC); } else { printf("EOT\n"); (void)fflush(stdout); } } if (fsize(mtf) == 0) { if (value("dontsendempty") != NULL) goto out; if (hp->h_subject == NULL) printf("No message, no subject; hope that's ok\n"); else printf("Null message body; hope that's ok\n"); } /* * Now, take the user names from the combined * to and cc lists and do all the alias * processing. */ senderr = 0; to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc))); if (to == NULL) { printf("No recipients specified\n"); senderr++; } /* * Look through the recipient list for names with /'s * in them which we write to as files directly. */ to = outof(to, mtf, hp); if (senderr) savedeadletter(mtf); to = elide(to); if (count(to) == 0) goto out; if (value("recordrecip") != NULL) { /* * Before fixing the header, save old To:. * We do this because elide above has sorted To: list, and * we would like to save message in a file named by the first * recipient the user has entered, not the one being the first * after sorting happened. */ if ((nsto = malloc(sizeof(struct name))) == NULL) err(1, "Out of memory"); bcopy(hp->h_to, nsto, sizeof(struct name)); } fixhead(hp, to); if ((mtf = infix(hp, mtf)) == NULL) { fprintf(stderr, ". . . message lost, sorry.\n"); return; } namelist = unpack(cat(hp->h_smopts, to)); if (debug) { char **t; printf("Sendmail arguments:"); for (t = namelist; *t != NULL; t++) printf(" \"%s\"", *t); printf("\n"); goto out; } if (value("recordrecip") != NULL) { /* * Extract first recipient username from saved To: and use it * as a filename. */ if ((nbuf = malloc(strlen(detract(nsto, 0)) + 1)) == NULL) err(1, "Out of memory"); if ((cp = yanklogin(detract(nsto, 0), nbuf)) != NULL) (void)savemail(expand(nbuf), mtf); free(nbuf); free(nsto); } else if ((cp = value("record")) != NULL) (void)savemail(expand(cp), mtf); /* * Fork, set up the temporary mail file as standard * input for "mail", and exec with the user list we generated * far above. */ pid = fork(); if (pid == -1) { warn("fork"); savedeadletter(mtf); goto out; } if (pid == 0) { sigset_t nset; (void)sigemptyset(&nset); (void)sigaddset(&nset, SIGHUP); (void)sigaddset(&nset, SIGINT); (void)sigaddset(&nset, SIGQUIT); (void)sigaddset(&nset, SIGTSTP); (void)sigaddset(&nset, SIGTTIN); (void)sigaddset(&nset, SIGTTOU); prepare_child(&nset, fileno(mtf), -1); if ((cp = value("sendmail")) != NULL) cp = expand(cp); else cp = _PATH_SENDMAIL; execv(cp, namelist); warn("%s", cp); _exit(1); } if (value("verbose") != NULL) (void)wait_child(pid); else free_child(pid); out: (void)Fclose(mtf); } /* * Fix the header by glopping all of the expanded names from * the distribution list into the appropriate fields. */ void fixhead(struct header *hp, struct name *tolist) { struct name *np; hp->h_to = NULL; hp->h_cc = NULL; hp->h_bcc = NULL; for (np = tolist; np != NULL; np = np->n_flink) { /* Don't copy deleted addresses to the header */ if (np->n_type & GDEL) continue; if ((np->n_type & GMASK) == GTO) hp->h_to = cat(hp->h_to, nalloc(np->n_name, np->n_type)); else if ((np->n_type & GMASK) == GCC) hp->h_cc = cat(hp->h_cc, nalloc(np->n_name, np->n_type)); else if ((np->n_type & GMASK) == GBCC) hp->h_bcc = cat(hp->h_bcc, nalloc(np->n_name, np->n_type)); } } /* * Prepend a header in front of the collected stuff * and return the new file. */ FILE * infix(struct header *hp, FILE *fi) { FILE *nfo, *nfi; int c, fd; char tempname[PATHSIZE]; (void)snprintf(tempname, sizeof(tempname), "%s/mail.RsXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (nfo = Fdopen(fd, "w")) == NULL) { warn("%s", tempname); return (fi); } if ((nfi = Fopen(tempname, "r")) == NULL) { warn("%s", tempname); (void)Fclose(nfo); (void)rm(tempname); return (fi); } (void)rm(tempname); (void)puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GREPLYTO|GINREPLYTO|GNL|GCOMMA); c = getc(fi); while (c != EOF) { (void)putc(c, nfo); c = getc(fi); } if (ferror(fi)) { warnx("read"); rewind(fi); return (fi); } (void)fflush(nfo); if (ferror(nfo)) { warn("%s", tempname); (void)Fclose(nfo); (void)Fclose(nfi); rewind(fi); return (fi); } (void)Fclose(nfo); (void)Fclose(fi); rewind(nfi); return (nfi); } /* * Dump the to, subject, cc header on the * passed file buffer. */ int puthead(struct header *hp, FILE *fo, int w) { int gotcha; gotcha = 0; if (hp->h_to != NULL && w & GTO) fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++; if (hp->h_subject != NULL && w & GSUBJECT) fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; if (hp->h_cc != NULL && w & GCC) fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++; if (hp->h_bcc != NULL && w & GBCC) fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++; if (hp->h_replyto != NULL && w & GREPLYTO) fprintf(fo, "Reply-To: %s\n", hp->h_replyto), gotcha++; if (hp->h_inreplyto != NULL && w & GINREPLYTO) fprintf(fo, "In-Reply-To: <%s>\n", hp->h_inreplyto), gotcha++; if (gotcha && w & GNL) (void)putc('\n', fo); return (0); } /* * Format the given header line to not exceed 72 characters. */ void fmt(const char *str, struct name *np, FILE *fo, int comma) { int col, len; comma = comma ? 1 : 0; col = strlen(str); if (col) fputs(str, fo); for (; np != NULL; np = np->n_flink) { if (np->n_flink == NULL) comma = 0; len = strlen(np->n_name); col++; /* for the space */ if (col + len + comma > 72 && col > 4) { fprintf(fo, "\n "); col = 4; } else fprintf(fo, " "); fputs(np->n_name, fo); if (comma) fprintf(fo, ","); col += len + comma; } fprintf(fo, "\n"); } /* * Save the outgoing mail on the passed file. */ /*ARGSUSED*/ int savemail(char name[], FILE *fi) { FILE *fo; char buf[BUFSIZ]; int i; time_t now; mode_t saved_umask; saved_umask = umask(077); fo = Fopen(name, "a"); umask(saved_umask); if (fo == NULL) { warn("%s", name); return (-1); } (void)time(&now); fprintf(fo, "From %s %s", myname, ctime(&now)); while ((i = fread(buf, 1, sizeof(buf), fi)) > 0) (void)fwrite(buf, 1, i, fo); fprintf(fo, "\n"); (void)fflush(fo); if (ferror(fo)) warn("%s", name); (void)Fclose(fo); rewind(fi); return (0); } diff --git a/usr.bin/mail/strings.c b/usr.bin/mail/strings.c index b8f7862c60e4..b3cc217dbded 100644 --- a/usr.bin/mail/strings.c +++ b/usr.bin/mail/strings.c @@ -1,123 +1,120 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include /* * Mail -- a mail program * * String allocation routines. * Strings handed out here are reclaimed at the top of the command * loop each time, so they need not be freed. */ #include "rcv.h" #include "extern.h" struct strings stringdope[NSPACE]; /* * Allocate size more bytes of space and return the address of the * first byte to the caller. An even number of bytes are always * allocated so that the space will always be on a word boundary. * The string spaces are of exponentially increasing size, to satisfy * the occasional user with enormous string size requests. */ char * salloc(int size) { char *t; int s, index; struct strings *sp; s = size; s += (sizeof(char *) - 1); s &= ~(sizeof(char *) - 1); index = 0; for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) { if (sp->s_topFree == NULL && (STRINGSIZE << index) >= s) break; if (sp->s_nleft >= s) break; index++; } if (sp >= &stringdope[NSPACE]) errx(1, "String too large"); if (sp->s_topFree == NULL) { index = sp - &stringdope[0]; if ((sp->s_topFree = malloc(STRINGSIZE << index)) == NULL) err(1, "No room for space %d", index); sp->s_nextFree = sp->s_topFree; sp->s_nleft = STRINGSIZE << index; } sp->s_nleft -= s; t = sp->s_nextFree; sp->s_nextFree += s; return (t); } /* * Reset the string area to be empty. * Called to free all strings allocated * since last reset. */ void sreset(void) { struct strings *sp; int index; if (noreset) return; index = 0; for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) { if (sp->s_topFree == NULL) continue; sp->s_nextFree = sp->s_topFree; sp->s_nleft = STRINGSIZE << index; index++; } } /* * Make the string area permanent. * Meant to be called in main, after initialization. */ void spreserve(void) { struct strings *sp; for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) sp->s_topFree = NULL; } diff --git a/usr.bin/mail/temp.c b/usr.bin/mail/temp.c index 7143d2130199..34a473af0e38 100644 --- a/usr.bin/mail/temp.c +++ b/usr.bin/mail/temp.c @@ -1,85 +1,82 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include "extern.h" /* * Mail -- a mail program * * Give names to all the temporary files that we will need. */ char *tmpdir; void tinit(void) { char *cp; if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0') tmpdir = _PATH_TMP; if ((tmpdir = strdup(tmpdir)) == NULL) errx(1, "Out of memory"); /* Strip trailing '/' if necessary */ cp = tmpdir + strlen(tmpdir) - 1; while (cp > tmpdir && *cp == '/') { *cp = '\0'; cp--; } /* * It's okay to call savestr in here because main will * do a spreserve() after us. */ if (myname != NULL) { if (getuserid(myname) < 0) errx(1, "\"%s\" is not a user of this system", myname); } else { if ((cp = username()) == NULL) { myname = "ubluit"; if (rcvmode) errx(1, "Who are you!?"); } else myname = savestr(cp); } if ((cp = getenv("HOME")) == NULL || *cp == '\0' || strlen(cp) >= PATHSIZE) homedir = NULL; else homedir = savestr(cp); if (debug) printf("user = %s, homedir = %s\n", myname, homedir ? homedir : "NONE"); } diff --git a/usr.bin/mail/tty.c b/usr.bin/mail/tty.c index 39804c9c1c9e..9921c8cbd209 100644 --- a/usr.bin/mail/tty.c +++ b/usr.bin/mail/tty.c @@ -1,291 +1,288 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include /* * Mail -- a mail program * * Generally useful tty stuff. */ #include "rcv.h" #include "extern.h" static cc_t c_erase; /* Current erase char */ static cc_t c_kill; /* Current kill char */ static jmp_buf rewrite; /* Place to go when continued */ static jmp_buf intjmp; /* Place to go when interrupted */ #ifndef TIOCSTI static int ttyset; /* We must now do erase/kill */ #endif /* * Read all relevant header fields. */ int grabh(struct header *hp, int gflags) { struct termios ttybuf; sig_t saveint; sig_t savetstp; sig_t savettou; sig_t savettin; int errs; #ifndef TIOCSTI sig_t savequit; #else # ifdef TIOCEXT int extproc, flag; # endif /* TIOCEXT */ #endif /* TIOCSTI */ savetstp = signal(SIGTSTP, SIG_DFL); savettou = signal(SIGTTOU, SIG_DFL); savettin = signal(SIGTTIN, SIG_DFL); errs = 0; #ifndef TIOCSTI ttyset = 0; #endif if (tcgetattr(fileno(stdin), &ttybuf) < 0) { warn("tcgetattr(stdin)"); return (-1); } c_erase = ttybuf.c_cc[VERASE]; c_kill = ttybuf.c_cc[VKILL]; #ifndef TIOCSTI ttybuf.c_cc[VERASE] = _POSIX_VDISABLE; ttybuf.c_cc[VKILL] = _POSIX_VDISABLE; if ((saveint = signal(SIGINT, SIG_IGN)) == SIG_DFL) (void)signal(SIGINT, SIG_DFL); if ((savequit = signal(SIGQUIT, SIG_IGN)) == SIG_DFL) (void)signal(SIGQUIT, SIG_DFL); #else # ifdef TIOCEXT extproc = ((ttybuf.c_lflag & EXTPROC) ? 1 : 0); if (extproc) { flag = 0; if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) warn("TIOCEXT: off"); } # endif /* TIOCEXT */ if (setjmp(intjmp)) goto out; saveint = signal(SIGINT, ttyint); #endif if (gflags & GTO) { #ifndef TIOCSTI if (!ttyset && hp->h_to != NULL) ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); #endif hp->h_to = extract(readtty("To: ", detract(hp->h_to, 0)), GTO); } if (gflags & GSUBJECT) { #ifndef TIOCSTI if (!ttyset && hp->h_subject != NULL) ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); #endif hp->h_subject = readtty("Subject: ", hp->h_subject); } if (gflags & GCC) { #ifndef TIOCSTI if (!ttyset && hp->h_cc != NULL) ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); #endif hp->h_cc = extract(readtty("Cc: ", detract(hp->h_cc, 0)), GCC); } if (gflags & GBCC) { #ifndef TIOCSTI if (!ttyset && hp->h_bcc != NULL) ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); #endif hp->h_bcc = extract(readtty("Bcc: ", detract(hp->h_bcc, 0)), GBCC); } #ifdef TIOCSTI out: #endif (void)signal(SIGTSTP, savetstp); (void)signal(SIGTTOU, savettou); (void)signal(SIGTTIN, savettin); #ifndef TIOCSTI ttybuf.c_cc[VERASE] = c_erase; ttybuf.c_cc[VKILL] = c_kill; if (ttyset) tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf); (void)signal(SIGQUIT, savequit); #else # ifdef TIOCEXT if (extproc) { flag = 1; if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0) warn("TIOCEXT: on"); } # endif /* TIOCEXT */ #endif (void)signal(SIGINT, saveint); return (errs); } /* * Read up a header from standard input. * The source string has the preliminary contents to * be read. * */ char * readtty(const char *pr, char src[]) { char canonb[BUFSIZ]; #ifdef TIOCSTI char ch; #endif int c; char *cp, *cp2; fputs(pr, stdout); (void)fflush(stdout); if (src != NULL && strlen(src) > BUFSIZ - 2) { printf("too long to edit\n"); return (src); } #ifndef TIOCSTI if (src != NULL) strlcpy(canonb, src, sizeof(canonb)); else *canonb = '\0'; fputs(canonb, stdout); (void)fflush(stdout); #else cp = src == NULL ? "" : src; while ((c = *cp++) != '\0') { if ((c_erase != _POSIX_VDISABLE && c == c_erase) || (c_kill != _POSIX_VDISABLE && c == c_kill)) { ch = '\\'; ioctl(0, TIOCSTI, &ch); } ch = c; ioctl(0, TIOCSTI, &ch); } cp = canonb; *cp = '\0'; #endif cp2 = cp; while (cp2 < canonb + BUFSIZ) *cp2++ = '\0'; cp2 = cp; if (setjmp(rewrite)) goto redo; (void)signal(SIGTSTP, ttystop); (void)signal(SIGTTOU, ttystop); (void)signal(SIGTTIN, ttystop); clearerr(stdin); while (cp2 < canonb + BUFSIZ) { c = getc(stdin); if (c == EOF || c == '\n') break; *cp2++ = c; } *cp2 = '\0'; (void)signal(SIGTSTP, SIG_DFL); (void)signal(SIGTTOU, SIG_DFL); (void)signal(SIGTTIN, SIG_DFL); if (c == EOF && ferror(stdin)) { redo: cp = strlen(canonb) > 0 ? canonb : NULL; clearerr(stdin); return (readtty(pr, cp)); } #ifndef TIOCSTI if (cp == NULL || *cp == '\0') return (src); cp2 = cp; if (!ttyset) return (strlen(canonb) > 0 ? savestr(canonb) : NULL); while (*cp != '\0') { c = *cp++; if (c_erase != _POSIX_VDISABLE && c == c_erase) { if (cp2 == canonb) continue; if (cp2[-1] == '\\') { cp2[-1] = c; continue; } cp2--; continue; } if (c_kill != _POSIX_VDISABLE && c == c_kill) { if (cp2 == canonb) continue; if (cp2[-1] == '\\') { cp2[-1] = c; continue; } cp2 = canonb; continue; } *cp2++ = c; } *cp2 = '\0'; #endif if (equal("", canonb)) return (NULL); return (savestr(canonb)); } /* * Receipt continuation. */ void ttystop(int s) { sig_t old_action = signal(s, SIG_DFL); sigset_t nset; (void)sigemptyset(&nset); (void)sigaddset(&nset, s); (void)sigprocmask(SIG_BLOCK, &nset, NULL); kill(0, s); (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); (void)signal(s, old_action); longjmp(rewrite, 1); } void ttyint(int s __unused) { longjmp(intjmp, 1); } diff --git a/usr.bin/mail/util.c b/usr.bin/mail/util.c index af61b9c48382..ce53c7108a0d 100644 --- a/usr.bin/mail/util.c +++ b/usr.bin/mail/util.c @@ -1,596 +1,593 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include #include #include "rcv.h" #include "extern.h" /* * Mail -- a mail program * * Auxiliary functions. */ static char *save2str(char *, char *); /* * Return a pointer to a dynamic copy of the argument. */ char * savestr(char *str) { char *new; int size = strlen(str) + 1; if ((new = salloc(size)) != NULL) bcopy(str, new, size); return (new); } /* * Make a copy of new argument incorporating old one. */ static char * save2str(char *str, char *old) { char *new; int newsize = strlen(str) + 1; int oldsize = old ? strlen(old) + 1 : 0; if ((new = salloc(newsize + oldsize)) != NULL) { if (oldsize) { bcopy(old, new, oldsize); new[oldsize - 1] = ' '; } bcopy(str, new + oldsize, newsize); } return (new); } /* * Touch the named message by setting its MTOUCH flag. * Touched messages have the effect of not being sent * back to the system mailbox on exit. */ void touch(struct message *mp) { mp->m_flag |= MTOUCH; if ((mp->m_flag & MREAD) == 0) mp->m_flag |= MREAD|MSTATUS; } /* * Test to see if the passed file name is a directory. * Return true if it is. */ int isdir(char name[]) { struct stat sbuf; if (stat(name, &sbuf) < 0) return (0); return (S_ISDIR(sbuf.st_mode)); } /* * Count the number of arguments in the given string raw list. */ int argcount(char **argv) { char **ap; for (ap = argv; *ap++ != NULL;) ; return (ap - argv - 1); } /* * Return the desired header line from the passed message * pointer (or NULL if the desired header field is not available). */ char * hfield(const char *field, struct message *mp) { FILE *ibuf; char linebuf[LINESIZE]; int lc; char *hfield; char *colon, *oldhfield = NULL; ibuf = setinput(mp); if ((lc = mp->m_lines - 1) < 0) return (NULL); if (readline(ibuf, linebuf, LINESIZE) < 0) return (NULL); while (lc > 0) { if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0) return (oldhfield); if ((hfield = ishfield(linebuf, colon, field)) != NULL) oldhfield = save2str(hfield, oldhfield); } return (oldhfield); } /* * Return the next header field found in the given message. * Return >= 0 if something found, < 0 elsewise. * "colon" is set to point to the colon in the header. * Must deal with \ continuations & other such fraud. */ int gethfield(FILE *f, char linebuf[], int rem, char **colon) { char line2[LINESIZE]; char *cp, *cp2; int c; for (;;) { if (--rem < 0) return (-1); if ((c = readline(f, linebuf, LINESIZE)) <= 0) return (-1); for (cp = linebuf; isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':'; cp++) ; if (*cp != ':' || cp == linebuf) continue; /* * I guess we got a headline. * Handle wraparounding */ *colon = cp; cp = linebuf + c; for (;;) { while (--cp >= linebuf && (*cp == ' ' || *cp == '\t')) ; cp++; if (rem <= 0) break; ungetc(c = getc(f), f); if (c != ' ' && c != '\t') break; if ((c = readline(f, line2, LINESIZE)) < 0) break; rem--; for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++) ; c -= cp2 - line2; if (cp + c >= linebuf + LINESIZE - 2) break; *cp++ = ' '; bcopy(cp2, cp, c); cp += c; } *cp = 0; return (rem); } /* NOTREACHED */ } /* * Check whether the passed line is a header line of * the desired breed. Return the field body, or 0. */ char* ishfield(char *linebuf, char *colon, const char *field) { char *cp = colon; *cp = 0; if (strcasecmp(linebuf, field) != 0) { *cp = ':'; return (0); } *cp = ':'; for (cp++; *cp == ' ' || *cp == '\t'; cp++) ; return (cp); } /* * Copy a string and lowercase the result. * dsize: space left in buffer (including space for NULL) */ void istrncpy(char *dest, const char *src, size_t dsize) { strlcpy(dest, src, dsize); for (; *dest; dest++) *dest = tolower((unsigned char)*dest); } /* * The following code deals with input stacking to do source * commands. All but the current file pointer are saved on * the stack. */ static int ssp; /* Top of file stack */ struct sstack { FILE *s_file; /* File we were in. */ int s_cond; /* Saved state of conditionals */ int s_loading; /* Loading .mailrc, etc. */ }; #define SSTACK_SIZE 64 /* XXX was NOFILE. */ static struct sstack sstack[SSTACK_SIZE]; /* * Pushdown current input file and switch to a new one. * Set the global flag "sourcing" so that others will realize * that they are no longer reading from a tty (in all probability). */ int source(void *arg) { char **arglist = arg; FILE *fi; char *cp; if ((cp = expand(*arglist)) == NULL) return (1); if ((fi = Fopen(cp, "r")) == NULL) { warn("%s", cp); return (1); } if (ssp >= SSTACK_SIZE - 1) { printf("Too much \"sourcing\" going on.\n"); (void)Fclose(fi); return (1); } sstack[ssp].s_file = input; sstack[ssp].s_cond = cond; sstack[ssp].s_loading = loading; ssp++; loading = 0; cond = CANY; input = fi; sourcing++; return (0); } /* * Pop the current input back to the previous level. * Update the "sourcing" flag as appropriate. */ int unstack(void) { if (ssp <= 0) { printf("\"Source\" stack over-pop.\n"); sourcing = 0; return (1); } (void)Fclose(input); if (cond != CANY) printf("Unmatched \"if\"\n"); ssp--; cond = sstack[ssp].s_cond; loading = sstack[ssp].s_loading; input = sstack[ssp].s_file; if (ssp == 0) sourcing = loading; return (0); } /* * Touch the indicated file. * This is nifty for the shell. */ void alter(char *name) { struct timespec ts[2]; (void)clock_gettime(CLOCK_REALTIME, &ts[0]); ts[0].tv_sec++; ts[1].tv_sec = 0; ts[1].tv_nsec = UTIME_OMIT; (void)utimensat(AT_FDCWD, name, ts, 0); } /* * Get sender's name from this message. If the message has * a bunch of arpanet stuff in it, we may have to skin the name * before returning it. */ char * nameof(struct message *mp, int reptype) { char *cp, *cp2; cp = skin(name1(mp, reptype)); if (reptype != 0 || charcount(cp, '!') < 2) return (cp); cp2 = strrchr(cp, '!'); cp2--; while (cp2 > cp && *cp2 != '!') cp2--; if (*cp2 == '!') return (cp2 + 1); return (cp); } /* * Start of a "comment". * Ignore it. */ char * skip_comment(char *cp) { int nesting = 1; for (; nesting > 0 && *cp; cp++) { switch (*cp) { case '\\': if (cp[1]) cp++; break; case '(': nesting++; break; case ')': nesting--; break; } } return (cp); } /* * Skin an arpa net address according to the RFC 822 interpretation * of "host-phrase." */ char * skin(char *name) { char *nbuf, *bufend, *cp, *cp2; int c, gotlt, lastsp; if (name == NULL) return (NULL); if (strchr(name, '(') == NULL && strchr(name, '<') == NULL && strchr(name, ' ') == NULL) return (name); /* We assume that length(input) <= length(output) */ if ((nbuf = malloc(strlen(name) + 1)) == NULL) err(1, "Out of memory"); gotlt = 0; lastsp = 0; bufend = nbuf; for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) { switch (c) { case '(': cp = skip_comment(cp); lastsp = 0; break; case '"': /* * Start of a "quoted-string". * Copy it in its entirety. */ while ((c = *cp) != '\0') { cp++; if (c == '"') break; if (c != '\\') *cp2++ = c; else if ((c = *cp) != '\0') { *cp2++ = c; cp++; } } lastsp = 0; break; case ' ': if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') cp += 3, *cp2++ = '@'; else if (cp[0] == '@' && cp[1] == ' ') cp += 2, *cp2++ = '@'; else lastsp = 1; break; case '<': cp2 = bufend; gotlt++; lastsp = 0; break; case '>': if (gotlt) { gotlt = 0; while ((c = *cp) != '\0' && c != ',') { cp++; if (c == '(') cp = skip_comment(cp); else if (c == '"') while ((c = *cp) != '\0') { cp++; if (c == '"') break; if (c == '\\' && *cp != '\0') cp++; } } lastsp = 0; break; } /* FALLTHROUGH */ default: if (lastsp) { lastsp = 0; *cp2++ = ' '; } *cp2++ = c; if (c == ',' && !gotlt && (*cp == ' ' || *cp == '"' || *cp == '<')) { *cp2++ = ' '; while (*cp == ' ') cp++; lastsp = 0; bufend = cp2; } } } *cp2 = '\0'; if ((cp = realloc(nbuf, strlen(nbuf) + 1)) != NULL) nbuf = cp; return (nbuf); } /* * Fetch the sender's name from the passed message. * Reptype can be * 0 -- get sender's name for display purposes * 1 -- get sender's name for reply * 2 -- get sender's name for Reply */ char * name1(struct message *mp, int reptype) { char namebuf[LINESIZE]; char linebuf[LINESIZE]; char *cp, *cp2; FILE *ibuf; int first = 1; if ((cp = hfield("from", mp)) != NULL) return (cp); if (reptype == 0 && (cp = hfield("sender", mp)) != NULL) return (cp); ibuf = setinput(mp); namebuf[0] = '\0'; if (readline(ibuf, linebuf, LINESIZE) < 0) return (savestr(namebuf)); newname: for (cp = linebuf; *cp != '\0' && *cp != ' '; cp++) ; for (; *cp == ' ' || *cp == '\t'; cp++) ; for (cp2 = &namebuf[strlen(namebuf)]; *cp != '\0' && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) *cp2++ = *cp++; *cp2 = '\0'; if (readline(ibuf, linebuf, LINESIZE) < 0) return (savestr(namebuf)); if ((cp = strchr(linebuf, 'F')) == NULL) return (savestr(namebuf)); if (strncmp(cp, "From", 4) != 0) return (savestr(namebuf)); while ((cp = strchr(cp, 'r')) != NULL) { if (strncmp(cp, "remote", 6) == 0) { if ((cp = strchr(cp, 'f')) == NULL) break; if (strncmp(cp, "from", 4) != 0) break; if ((cp = strchr(cp, ' ')) == NULL) break; cp++; if (first) { cp2 = namebuf; first = 0; } else cp2 = strrchr(namebuf, '!') + 1; strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1); strcat(namebuf, "!"); goto newname; } cp++; } return (savestr(namebuf)); } /* * Count the occurrences of c in str */ int charcount(char *str, int c) { char *cp; int i; for (i = 0, cp = str; *cp != '\0'; cp++) if (*cp == c) i++; return (i); } /* * See if the given header field is supposed to be ignored. */ int isign(const char *field, struct ignoretab ignore[2]) { char realfld[LINESIZE]; if (ignore == ignoreall) return (1); /* * Lower-case the string, so that "Status" and "status" * will hash to the same place. */ istrncpy(realfld, field, sizeof(realfld)); if (ignore[1].i_count > 0) return (!member(realfld, ignore + 1)); else return (member(realfld, ignore)); } int member(char *realfield, struct ignoretab *table) { struct ignore *igp; for (igp = table->i_head[hash(realfield)]; igp != NULL; igp = igp->i_link) if (*igp->i_field == *realfield && equal(igp->i_field, realfield)) return (1); return (0); } diff --git a/usr.bin/mail/v7.local.c b/usr.bin/mail/v7.local.c index 642e8af1f4e8..6390b4235ace 100644 --- a/usr.bin/mail/v7.local.c +++ b/usr.bin/mail/v7.local.c @@ -1,93 +1,90 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include /* * Mail -- a mail program * * Version 7 * * Local routines that are installation dependent. */ #include "rcv.h" #include #include "extern.h" /* * Locate the user's mailbox file (ie, the place where new, unread * mail is queued). */ void findmail(char *user, char *buf, int buflen) { char *tmp = getenv("MAIL"); if (tmp == NULL) (void)snprintf(buf, buflen, "%s/%s", _PATH_MAILDIR, user); else (void)strlcpy(buf, tmp, buflen); } /* * Get rid of the queued mail. */ void demail(void) { int fd; if (value("keep") != NULL || rm(mailname) < 0) if ((fd = open(mailname, O_CREAT | O_TRUNC | O_WRONLY, 0600)) >= 0) (void)close(fd); } /* * Discover user login name. */ char * username(void) { char *np; uid_t uid; if ((np = getenv("USER")) != NULL) return (np); if ((np = getenv("LOGNAME")) != NULL) return (np); if ((np = getname(uid = getuid())) != NULL) return (np); printf("Cannot associate a name with uid %u\n", (unsigned)uid); return (NULL); } diff --git a/usr.bin/mail/vars.c b/usr.bin/mail/vars.c index f65591fd5046..e55ac7c37779 100644 --- a/usr.bin/mail/vars.c +++ b/usr.bin/mail/vars.c @@ -1,180 +1,177 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include #include "rcv.h" #include "extern.h" /* * Mail -- a mail program * * Variable handling stuff. */ /* * Assign a value to a variable. */ void assign(const char *name, const char *value) { struct var *vp; int h; h = hash(name); vp = lookup(name); if (vp == NULL) { if ((vp = calloc(1, sizeof(*vp))) == NULL) err(1, "Out of memory"); vp->v_name = vcopy(name); vp->v_link = variables[h]; variables[h] = vp; } else vfree(vp->v_value); vp->v_value = vcopy(value); } /* * Free up a variable string. We do not bother to allocate * strings whose value is "" since they are expected to be frequent. * Thus, we cannot free same! */ void vfree(char *cp) { if (*cp != '\0') (void)free(cp); } /* * Copy a variable value into permanent (ie, not collected after each * command) space. Do not bother to alloc space for "" */ char * vcopy(const char *str) { char *new; unsigned len; if (*str == '\0') return (""); len = strlen(str) + 1; if ((new = malloc(len)) == NULL) err(1, "Out of memory"); bcopy(str, new, (int)len); return (new); } /* * Get the value of a variable and return it. * Look in the environment if its not available locally. */ char * value(const char *name) { struct var *vp; if ((vp = lookup(name)) == NULL) return (getenv(name)); return (vp->v_value); } /* * Locate a variable and return its variable * node. */ struct var * lookup(const char *name) { struct var *vp; for (vp = variables[hash(name)]; vp != NULL; vp = vp->v_link) if (*vp->v_name == *name && equal(vp->v_name, name)) return (vp); return (NULL); } /* * Locate a group name and return it. */ struct grouphead * findgroup(char name[]) { struct grouphead *gh; for (gh = groups[hash(name)]; gh != NULL; gh = gh->g_link) if (*gh->g_name == *name && equal(gh->g_name, name)) return (gh); return (NULL); } /* * Print a group out on stdout */ void printgroup(char name[]) { struct grouphead *gh; struct group *gp; if ((gh = findgroup(name)) == NULL) { printf("\"%s\": not a group\n", name); return; } printf("%s\t", gh->g_name); for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) printf(" %s", gp->ge_name); printf("\n"); } /* * Hash the passed string and return an index into * the variable or group hash table. */ int hash(const char *name) { int h = 0; while (*name != '\0') { h <<= 2; h += *name++; } if (h < 0 && (h = -h) < 0) h = 0; return (h % HSHSIZE); } diff --git a/usr.bin/mail/version.c b/usr.bin/mail/version.c index 6179b9cdcccc..c44f8539a73a 100644 --- a/usr.bin/mail/version.c +++ b/usr.bin/mail/version.c @@ -1,39 +1,36 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ -#include /* * Just keep track of the date/sid of this version of Mail. * Load this file first to get a "total" Mail version. */ const char *version = "8.1 6/6/93"; diff --git a/usr.bin/ministat/ministat.c b/usr.bin/ministat/ministat.c index 32472a8acee7..6508ce3f2533 100644 --- a/usr.bin/ministat/ministat.c +++ b/usr.bin/ministat/ministat.c @@ -1,692 +1,691 @@ /*- * SPDX-License-Identifier: Beerware * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NSTUDENT 100 #define NCONF 6 static double const studentpct[] = { 80, 90, 95, 98, 99, 99.5 }; static double const student[NSTUDENT + 1][NCONF] = { /* inf */ { 1.282, 1.645, 1.960, 2.326, 2.576, 3.090 }, /* 1. */ { 3.078, 6.314, 12.706, 31.821, 63.657, 318.313 }, /* 2. */ { 1.886, 2.920, 4.303, 6.965, 9.925, 22.327 }, /* 3. */ { 1.638, 2.353, 3.182, 4.541, 5.841, 10.215 }, /* 4. */ { 1.533, 2.132, 2.776, 3.747, 4.604, 7.173 }, /* 5. */ { 1.476, 2.015, 2.571, 3.365, 4.032, 5.893 }, /* 6. */ { 1.440, 1.943, 2.447, 3.143, 3.707, 5.208 }, /* 7. */ { 1.415, 1.895, 2.365, 2.998, 3.499, 4.782 }, /* 8. */ { 1.397, 1.860, 2.306, 2.896, 3.355, 4.499 }, /* 9. */ { 1.383, 1.833, 2.262, 2.821, 3.250, 4.296 }, /* 10. */ { 1.372, 1.812, 2.228, 2.764, 3.169, 4.143 }, /* 11. */ { 1.363, 1.796, 2.201, 2.718, 3.106, 4.024 }, /* 12. */ { 1.356, 1.782, 2.179, 2.681, 3.055, 3.929 }, /* 13. */ { 1.350, 1.771, 2.160, 2.650, 3.012, 3.852 }, /* 14. */ { 1.345, 1.761, 2.145, 2.624, 2.977, 3.787 }, /* 15. */ { 1.341, 1.753, 2.131, 2.602, 2.947, 3.733 }, /* 16. */ { 1.337, 1.746, 2.120, 2.583, 2.921, 3.686 }, /* 17. */ { 1.333, 1.740, 2.110, 2.567, 2.898, 3.646 }, /* 18. */ { 1.330, 1.734, 2.101, 2.552, 2.878, 3.610 }, /* 19. */ { 1.328, 1.729, 2.093, 2.539, 2.861, 3.579 }, /* 20. */ { 1.325, 1.725, 2.086, 2.528, 2.845, 3.552 }, /* 21. */ { 1.323, 1.721, 2.080, 2.518, 2.831, 3.527 }, /* 22. */ { 1.321, 1.717, 2.074, 2.508, 2.819, 3.505 }, /* 23. */ { 1.319, 1.714, 2.069, 2.500, 2.807, 3.485 }, /* 24. */ { 1.318, 1.711, 2.064, 2.492, 2.797, 3.467 }, /* 25. */ { 1.316, 1.708, 2.060, 2.485, 2.787, 3.450 }, /* 26. */ { 1.315, 1.706, 2.056, 2.479, 2.779, 3.435 }, /* 27. */ { 1.314, 1.703, 2.052, 2.473, 2.771, 3.421 }, /* 28. */ { 1.313, 1.701, 2.048, 2.467, 2.763, 3.408 }, /* 29. */ { 1.311, 1.699, 2.045, 2.462, 2.756, 3.396 }, /* 30. */ { 1.310, 1.697, 2.042, 2.457, 2.750, 3.385 }, /* 31. */ { 1.309, 1.696, 2.040, 2.453, 2.744, 3.375 }, /* 32. */ { 1.309, 1.694, 2.037, 2.449, 2.738, 3.365 }, /* 33. */ { 1.308, 1.692, 2.035, 2.445, 2.733, 3.356 }, /* 34. */ { 1.307, 1.691, 2.032, 2.441, 2.728, 3.348 }, /* 35. */ { 1.306, 1.690, 2.030, 2.438, 2.724, 3.340 }, /* 36. */ { 1.306, 1.688, 2.028, 2.434, 2.719, 3.333 }, /* 37. */ { 1.305, 1.687, 2.026, 2.431, 2.715, 3.326 }, /* 38. */ { 1.304, 1.686, 2.024, 2.429, 2.712, 3.319 }, /* 39. */ { 1.304, 1.685, 2.023, 2.426, 2.708, 3.313 }, /* 40. */ { 1.303, 1.684, 2.021, 2.423, 2.704, 3.307 }, /* 41. */ { 1.303, 1.683, 2.020, 2.421, 2.701, 3.301 }, /* 42. */ { 1.302, 1.682, 2.018, 2.418, 2.698, 3.296 }, /* 43. */ { 1.302, 1.681, 2.017, 2.416, 2.695, 3.291 }, /* 44. */ { 1.301, 1.680, 2.015, 2.414, 2.692, 3.286 }, /* 45. */ { 1.301, 1.679, 2.014, 2.412, 2.690, 3.281 }, /* 46. */ { 1.300, 1.679, 2.013, 2.410, 2.687, 3.277 }, /* 47. */ { 1.300, 1.678, 2.012, 2.408, 2.685, 3.273 }, /* 48. */ { 1.299, 1.677, 2.011, 2.407, 2.682, 3.269 }, /* 49. */ { 1.299, 1.677, 2.010, 2.405, 2.680, 3.265 }, /* 50. */ { 1.299, 1.676, 2.009, 2.403, 2.678, 3.261 }, /* 51. */ { 1.298, 1.675, 2.008, 2.402, 2.676, 3.258 }, /* 52. */ { 1.298, 1.675, 2.007, 2.400, 2.674, 3.255 }, /* 53. */ { 1.298, 1.674, 2.006, 2.399, 2.672, 3.251 }, /* 54. */ { 1.297, 1.674, 2.005, 2.397, 2.670, 3.248 }, /* 55. */ { 1.297, 1.673, 2.004, 2.396, 2.668, 3.245 }, /* 56. */ { 1.297, 1.673, 2.003, 2.395, 2.667, 3.242 }, /* 57. */ { 1.297, 1.672, 2.002, 2.394, 2.665, 3.239 }, /* 58. */ { 1.296, 1.672, 2.002, 2.392, 2.663, 3.237 }, /* 59. */ { 1.296, 1.671, 2.001, 2.391, 2.662, 3.234 }, /* 60. */ { 1.296, 1.671, 2.000, 2.390, 2.660, 3.232 }, /* 61. */ { 1.296, 1.670, 2.000, 2.389, 2.659, 3.229 }, /* 62. */ { 1.295, 1.670, 1.999, 2.388, 2.657, 3.227 }, /* 63. */ { 1.295, 1.669, 1.998, 2.387, 2.656, 3.225 }, /* 64. */ { 1.295, 1.669, 1.998, 2.386, 2.655, 3.223 }, /* 65. */ { 1.295, 1.669, 1.997, 2.385, 2.654, 3.220 }, /* 66. */ { 1.295, 1.668, 1.997, 2.384, 2.652, 3.218 }, /* 67. */ { 1.294, 1.668, 1.996, 2.383, 2.651, 3.216 }, /* 68. */ { 1.294, 1.668, 1.995, 2.382, 2.650, 3.214 }, /* 69. */ { 1.294, 1.667, 1.995, 2.382, 2.649, 3.213 }, /* 70. */ { 1.294, 1.667, 1.994, 2.381, 2.648, 3.211 }, /* 71. */ { 1.294, 1.667, 1.994, 2.380, 2.647, 3.209 }, /* 72. */ { 1.293, 1.666, 1.993, 2.379, 2.646, 3.207 }, /* 73. */ { 1.293, 1.666, 1.993, 2.379, 2.645, 3.206 }, /* 74. */ { 1.293, 1.666, 1.993, 2.378, 2.644, 3.204 }, /* 75. */ { 1.293, 1.665, 1.992, 2.377, 2.643, 3.202 }, /* 76. */ { 1.293, 1.665, 1.992, 2.376, 2.642, 3.201 }, /* 77. */ { 1.293, 1.665, 1.991, 2.376, 2.641, 3.199 }, /* 78. */ { 1.292, 1.665, 1.991, 2.375, 2.640, 3.198 }, /* 79. */ { 1.292, 1.664, 1.990, 2.374, 2.640, 3.197 }, /* 80. */ { 1.292, 1.664, 1.990, 2.374, 2.639, 3.195 }, /* 81. */ { 1.292, 1.664, 1.990, 2.373, 2.638, 3.194 }, /* 82. */ { 1.292, 1.664, 1.989, 2.373, 2.637, 3.193 }, /* 83. */ { 1.292, 1.663, 1.989, 2.372, 2.636, 3.191 }, /* 84. */ { 1.292, 1.663, 1.989, 2.372, 2.636, 3.190 }, /* 85. */ { 1.292, 1.663, 1.988, 2.371, 2.635, 3.189 }, /* 86. */ { 1.291, 1.663, 1.988, 2.370, 2.634, 3.188 }, /* 87. */ { 1.291, 1.663, 1.988, 2.370, 2.634, 3.187 }, /* 88. */ { 1.291, 1.662, 1.987, 2.369, 2.633, 3.185 }, /* 89. */ { 1.291, 1.662, 1.987, 2.369, 2.632, 3.184 }, /* 90. */ { 1.291, 1.662, 1.987, 2.368, 2.632, 3.183 }, /* 91. */ { 1.291, 1.662, 1.986, 2.368, 2.631, 3.182 }, /* 92. */ { 1.291, 1.662, 1.986, 2.368, 2.630, 3.181 }, /* 93. */ { 1.291, 1.661, 1.986, 2.367, 2.630, 3.180 }, /* 94. */ { 1.291, 1.661, 1.986, 2.367, 2.629, 3.179 }, /* 95. */ { 1.291, 1.661, 1.985, 2.366, 2.629, 3.178 }, /* 96. */ { 1.290, 1.661, 1.985, 2.366, 2.628, 3.177 }, /* 97. */ { 1.290, 1.661, 1.985, 2.365, 2.627, 3.176 }, /* 98. */ { 1.290, 1.661, 1.984, 2.365, 2.627, 3.175 }, /* 99. */ { 1.290, 1.660, 1.984, 2.365, 2.626, 3.175 }, /* 100. */ { 1.290, 1.660, 1.984, 2.364, 2.626, 3.174 } }; #define MAX_DS 8 static char symbol[MAX_DS] = { ' ', 'x', '+', '*', '%', '#', '@', 'O' }; struct dataset { char *name; double *points; size_t lpoints; double sy, syy; size_t n; }; static struct dataset * NewSet(void) { struct dataset *ds; ds = calloc(1, sizeof *ds); assert(ds != NULL); ds->lpoints = 100000; ds->points = calloc(sizeof *ds->points, ds->lpoints); assert(ds->points != NULL); ds->syy = NAN; return(ds); } static void AddPoint(struct dataset *ds, double a) { double *dp; if (ds->n >= ds->lpoints) { dp = ds->points; ds->lpoints *= 4; ds->points = calloc(sizeof *ds->points, ds->lpoints); assert(ds->points != NULL); memcpy(ds->points, dp, sizeof *dp * ds->n); free(dp); } ds->points[ds->n++] = a; ds->sy += a; } static double Min(const struct dataset *ds) { return (ds->points[0]); } static double Max(const struct dataset *ds) { return (ds->points[ds->n -1]); } static double Avg(const struct dataset *ds) { return(ds->sy / ds->n); } static double Median(const struct dataset *ds) { const size_t m = ds->n / 2; if ((ds->n % 2) == 0) return ((ds->points[m] + (ds->points[m - 1])) / 2); return (ds->points[m]); } static double Var(struct dataset *ds) { size_t z; const double a = Avg(ds); if (isnan(ds->syy)) { ds->syy = 0.0; for (z = 0; z < ds->n; z++) ds->syy += (ds->points[z] - a) * (ds->points[z] - a); } return (ds->syy / (ds->n - 1.0)); } static double Stddev(struct dataset *ds) { return sqrt(Var(ds)); } static void VitalsHead(void) { printf(" N Min Max Median Avg Stddev\n"); } static void Vitals(struct dataset *ds, int flag) { printf("%c %3zu %13.8g %13.8g %13.8g %13.8g %13.8g", symbol[flag], ds->n, Min(ds), Max(ds), Median(ds), Avg(ds), Stddev(ds)); printf("\n"); } static void Relative(struct dataset *ds, struct dataset *rs, int confidx) { double spool, s, d, e, t; double re; size_t z; z = ds->n + rs->n - 2; if (z > NSTUDENT) t = student[0][confidx]; else t = student[z][confidx]; spool = (ds->n - 1) * Var(ds) + (rs->n - 1) * Var(rs); spool /= ds->n + rs->n - 2; spool = sqrt(spool); s = spool * sqrt(1.0 / ds->n + 1.0 / rs->n); d = Avg(ds) - Avg(rs); e = t * s; re = (ds->n - 1) * Var(ds) + (rs->n - 1) * Var(rs) * (Avg(ds) * Avg(ds)) / (Avg(rs) * Avg(rs)); re *= (ds->n + rs->n) / (ds->n * rs->n * (ds->n + rs->n - 2.0)); re = t * sqrt(re); if (fabs(d) > e) { printf("Difference at %.1f%% confidence\n", studentpct[confidx]); printf(" %g +/- %g\n", d, e); printf(" %g%% +/- %g%%\n", d * 100 / Avg(rs), re * 100 / Avg(rs)); printf(" (Student's t, pooled s = %g)\n", spool); } else { printf("No difference proven at %.1f%% confidence\n", studentpct[confidx]); } } struct plot { double min; double max; double span; int width; double x0, dx; size_t height; char *data; char **bar; int separate_bars; int num_datasets; }; static struct plot plot; static void SetupPlot(int width, int separate, int num_datasets) { struct plot *pl; pl = &plot; pl->width = width; pl->height = 0; pl->data = NULL; pl->bar = NULL; pl->separate_bars = separate; pl->num_datasets = num_datasets; pl->min = 999e99; pl->max = -999e99; } static void AdjPlot(double a) { struct plot *pl; pl = &plot; if (a < pl->min) pl->min = a; if (a > pl->max) pl->max = a; pl->span = pl->max - pl->min; pl->dx = pl->span / (pl->width - 1.0); pl->x0 = pl->min - .5 * pl->dx; } static void DimPlot(struct dataset *ds) { AdjPlot(Min(ds)); AdjPlot(Max(ds)); AdjPlot(Avg(ds) - Stddev(ds)); AdjPlot(Avg(ds) + Stddev(ds)); } static void PlotSet(struct dataset *ds, int val) { struct plot *pl; int i, x; size_t m, j, z; size_t n; int bar; double av, sd; pl = &plot; if (pl->span == 0) return; if (pl->separate_bars) bar = val-1; else bar = 0; if (pl->bar == NULL) { pl->bar = calloc(sizeof(char *), pl->num_datasets); assert(pl->bar != NULL); } if (pl->bar[bar] == NULL) { pl->bar[bar] = malloc(pl->width); assert(pl->bar[bar] != NULL); memset(pl->bar[bar], 0, pl->width); } m = 1; i = -1; j = 0; /* Set m to max(j) + 1, to allocate required memory */ for (n = 0; n < ds->n; n++) { x = (ds->points[n] - pl->x0) / pl->dx; if (x == i) { j++; if (j > m) m = j; } else { j = 1; i = x; } } m += 1; if (m > pl->height) { pl->data = realloc(pl->data, pl->width * m); assert(pl->data != NULL); memset(pl->data + pl->height * pl->width, 0, (m - pl->height) * pl->width); } pl->height = m; i = -1; for (n = 0; n < ds->n; n++) { x = (ds->points[n] - pl->x0) / pl->dx; if (x == i) { j++; } else { j = 1; i = x; } pl->data[j * pl->width + x] |= val; } av = Avg(ds); sd = Stddev(ds); if (!isnan(sd)) { x = ((av - sd) - pl->x0) / pl->dx; m = ((av + sd) - pl->x0) / pl->dx; pl->bar[bar][m] = '|'; pl->bar[bar][x] = '|'; for (z = x + 1; z < m; z++) if (pl->bar[bar][z] == 0) pl->bar[bar][z] = '_'; } x = (Median(ds) - pl->x0) / pl->dx; pl->bar[bar][x] = 'M'; x = (av - pl->x0) / pl->dx; pl->bar[bar][x] = 'A'; } static void DumpPlot(void) { struct plot *pl; int i, j, k; size_t z; pl = &plot; if (pl->span == 0) { printf("[no plot, span is zero width]\n"); return; } putchar('+'); for (i = 0; i < pl->width; i++) putchar('-'); putchar('+'); putchar('\n'); for (z = 1; z < pl->height; z++) { putchar('|'); for (j = 0; j < pl->width; j++) { k = pl->data[(pl->height - z) * pl->width + j]; if (k >= 0 && k < MAX_DS) putchar(symbol[k]); else printf("[%02x]", k); } putchar('|'); putchar('\n'); } for (i = 0; i < pl->num_datasets; i++) { if (pl->bar[i] == NULL) continue; putchar('|'); for (j = 0; j < pl->width; j++) { k = pl->bar[i][j]; if (k == 0) k = ' '; putchar(k); } putchar('|'); putchar('\n'); } putchar('+'); for (i = 0; i < pl->width; i++) putchar('-'); putchar('+'); putchar('\n'); } static int dbl_cmp(const void *a, const void *b) { const double *aa = a; const double *bb = b; if (*aa < *bb) return (-1); else if (*aa > *bb) return (1); else return (0); } static struct dataset * ReadSet(FILE *f, const char *n, int column, const char *delim) { char buf[BUFSIZ], *p, *t; struct dataset *s; double d; int line; int i; s = NewSet(); s->name = strdup(n); assert(s->name != NULL); line = 0; while (fgets(buf, sizeof buf, f) != NULL) { line++; i = strlen(buf); while (i > 0 && isspace(buf[i - 1])) buf[--i] = '\0'; for (i = 1, t = strtok(buf, delim); t != NULL && *t != '#'; i++, t = strtok(NULL, delim)) { if (i == column) break; } if (t == NULL || *t == '#') continue; d = strtod(t, &p); if (p != NULL && *p != '\0') errx(2, "Invalid data on line %d in %s", line, n); if (*buf != '\0') AddPoint(s, d); } if (s->n < 3) { fprintf(stderr, "Dataset %s must contain at least 3 data points\n", n); exit (2); } qsort(s->points, s->n, sizeof *s->points, dbl_cmp); return (s); } static void usage(char const *whine) { int i; fprintf(stderr, "%s\n", whine); fprintf(stderr, "Usage: ministat [-C column] [-c confidence] [-d delimiter(s)] [-Anqs] [-w width] [file [file ...]]\n"); fprintf(stderr, "\tconfidence = {"); for (i = 0; i < NCONF; i++) { fprintf(stderr, "%s%g%%", i ? ", " : "", studentpct[i]); } fprintf(stderr, "}\n"); fprintf(stderr, "\t-A : print statistics only. suppress the graph.\n"); fprintf(stderr, "\t-C : column number to extract (starts and defaults to 1)\n"); fprintf(stderr, "\t-d : delimiter(s) string, default to \" \\t\"\n"); fprintf(stderr, "\t-n : print summary statistics only, no graph/test\n"); fprintf(stderr, "\t-q : suppress printing summary-statistics headers and data-set names\n"); fprintf(stderr, "\t-s : print avg/median/stddev bars on separate lines\n"); fprintf(stderr, "\t-w : width of graph/test output (default 74 or terminal width)\n"); exit (2); } int main(int argc, char **argv) { const char *setfilenames[MAX_DS - 1]; struct dataset *ds[MAX_DS - 1]; FILE *setfiles[MAX_DS - 1]; int nds; double a; const char *delim = " \t"; char *p; int c, i, ci; int column = 1; int flag_s = 0; int flag_n = 0; int flag_q = 0; int termwidth = 74; int suppress_plot = 0; if (isatty(STDOUT_FILENO)) { struct winsize wsz; if ((p = getenv("COLUMNS")) != NULL && *p != '\0') termwidth = atoi(p); else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wsz) != -1 && wsz.ws_col > 0) termwidth = wsz.ws_col - 2; } ci = -1; while ((c = getopt(argc, argv, "AC:c:d:snqw:")) != -1) switch (c) { case 'A': suppress_plot = 1; break; case 'C': column = strtol(optarg, &p, 10); if (p != NULL && *p != '\0') usage("Invalid column number."); if (column <= 0) usage("Column number should be positive."); break; case 'c': a = strtod(optarg, &p); if (p != NULL && *p != '\0') usage("Not a floating point number"); for (i = 0; i < NCONF; i++) if (a == studentpct[i]) ci = i; if (ci == -1) usage("No support for confidence level"); break; case 'd': if (*optarg == '\0') usage("Can't use empty delimiter string"); delim = optarg; break; case 'n': flag_n = 1; break; case 'q': flag_q = 1; break; case 's': flag_s = 1; break; case 'w': termwidth = strtol(optarg, &p, 10); if (p != NULL && *p != '\0') usage("Invalid width, not a number."); if (termwidth < 0) usage("Unable to move beyond left margin."); break; default: usage("Unknown option"); break; } if (ci == -1) ci = 2; argc -= optind; argv += optind; if (argc == 0) { setfilenames[0] = ""; setfiles[0] = stdin; nds = 1; } else { if (argc > (MAX_DS - 1)) usage("Too many datasets."); nds = argc; for (i = 0; i < nds; i++) { setfilenames[i] = argv[i]; if (!strcmp(argv[i], "-")) setfiles[0] = stdin; else setfiles[i] = fopen(argv[i], "r"); if (setfiles[i] == NULL) err(2, "Cannot open %s", argv[i]); } } if (caph_limit_stdio() < 0) err(2, "capsicum"); for (i = 0; i < nds; i++) if (caph_limit_stream(fileno(setfiles[i]), CAPH_READ) < 0) err(2, "unable to limit rights for %s", setfilenames[i]); /* Enter Capsicum sandbox. */ if (caph_enter() < 0) err(2, "unable to enter capability mode"); for (i = 0; i < nds; i++) { ds[i] = ReadSet(setfiles[i], setfilenames[i], column, delim); if (setfiles[i] != stdin) fclose(setfiles[i]); } if (!flag_q) { for (i = 0; i < nds; i++) printf("%c %s\n", symbol[i+1], ds[i]->name); } if (!flag_n && !suppress_plot) { SetupPlot(termwidth, flag_s, nds); for (i = 0; i < nds; i++) DimPlot(ds[i]); for (i = 0; i < nds; i++) PlotSet(ds[i], i + 1); DumpPlot(); } if (!flag_q) VitalsHead(); Vitals(ds[0], 1); for (i = 1; i < nds; i++) { Vitals(ds[i], i + 1); if (!flag_n) Relative(ds[i], ds[0], ci); } exit(0); } diff --git a/usr.bin/mkcsmapper/yacc.y b/usr.bin/mkcsmapper/yacc.y index f048881b5b38..85bd525b315f 100644 --- a/usr.bin/mkcsmapper/yacc.y +++ b/usr.bin/mkcsmapper/yacc.y @@ -1,729 +1,728 @@ /* $NetBSD: yacc.y,v 1.11 2016/06/28 09:22:16 wiz Exp $ */ %{ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c)2003, 2006 Citrus Project, * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include "ldef.h" #ifndef __packed #define __packed #endif #include "citrus_namespace.h" #include "citrus_types.h" #include "citrus_mapper_std_file.h" #include "citrus_region.h" #include "citrus_db_factory.h" #include "citrus_db_hash.h" #include "citrus_lookup_factory.h" #include "citrus_pivot_factory.h" extern FILE *yyin; int debug = 0; static linear_zone_t rowcol[_CITRUS_MAPPER_STD_ROWCOL_MAX]; static char *map_name; static char *output = NULL; static void *table = NULL; static size_t rowcol_len = 0; static size_t table_size; static u_int32_t done_flag = 0; static u_int32_t dst_ilseq, dst_invalid, dst_unit_bits, oob_mode; static u_int32_t rowcol_bits = 0, rowcol_mask = 0; static u_int32_t src_next; static int map_type; static void (*putfunc)(void *, size_t, u_int32_t) = NULL; #define DF_TYPE 0x00000001 #define DF_NAME 0x00000002 #define DF_SRC_ZONE 0x00000004 #define DF_DST_INVALID 0x00000008 #define DF_DST_ILSEQ 0x00000010 #define DF_DST_UNIT_BITS 0x00000020 #define DF_OOB_MODE 0x00000040 static void dump_file(void); static void setup_map(void); static void set_type(int); static void set_name(char *); static void set_src_zone(u_int32_t); static void set_dst_invalid(u_int32_t); static void set_dst_ilseq(u_int32_t); static void set_dst_unit_bits(u_int32_t); static void set_oob_mode(u_int32_t); static int check_src(u_int32_t, u_int32_t); static void store(const linear_zone_t *, u_int32_t, int); static void put8(void *, size_t, u_int32_t); static void put16(void *, size_t, u_int32_t); static void put32(void *, size_t, u_int32_t); static void set_range(u_int32_t, u_int32_t); static void set_src(linear_zone_t *, u_int32_t, u_int32_t); %} %union { u_int32_t i_value; char *s_value; linear_zone_t lz_value; } %token R_TYPE R_NAME R_SRC_ZONE R_DST_UNIT_BITS %token R_DST_INVALID R_DST_ILSEQ %token R_BEGIN_MAP R_END_MAP R_INVALID R_ROWCOL %token R_ILSEQ R_OOB_MODE %token R_LN %token L_IMM %token L_STRING %type src %type dst types oob_mode_sel zone %% file : property mapping lns { dump_file(); } property : /* empty */ | property R_LN | property name | property type | property src_zone | property dst_invalid | property dst_ilseq | property dst_unit_bits | property oob_mode name : R_NAME L_STRING { set_name($2); $2 = NULL; } type : R_TYPE types { set_type($2); } types : R_ROWCOL { $$ = R_ROWCOL; } range : L_IMM '-' L_IMM { set_range($1, $3); } ranges : /* empty */ | ranges range '/' src_zone : R_SRC_ZONE zone { set_src_zone($2); } zone : range { $$ = 32; } | range '/' range '/' ranges L_IMM { $$ = $6; } dst_invalid : R_DST_INVALID L_IMM { set_dst_invalid($2); } dst_ilseq : R_DST_ILSEQ L_IMM { set_dst_ilseq($2); } dst_unit_bits : R_DST_UNIT_BITS L_IMM { set_dst_unit_bits($2); } oob_mode : R_OOB_MODE oob_mode_sel { set_oob_mode($2); } oob_mode_sel : R_INVALID { $$ = _CITRUS_MAPPER_STD_OOB_NONIDENTICAL; } | R_ILSEQ { $$ = _CITRUS_MAPPER_STD_OOB_ILSEQ; } mapping : begin_map map_elems R_END_MAP begin_map : R_BEGIN_MAP lns { setup_map(); } map_elems : /* empty */ | map_elems map_elem lns map_elem : src '=' dst { store(&$1, $3, 0); } | src '=' L_IMM '-' { store(&$1, $3, 1); } dst : L_IMM { $$ = $1; } | R_INVALID { $$ = dst_invalid; } | R_ILSEQ { $$ = dst_ilseq; } src : /* empty */ { set_src(&$$, src_next, src_next); } | L_IMM { set_src(&$$, $1, $1); } | L_IMM '-' L_IMM { set_src(&$$, $1, $3); } | '-' L_IMM { set_src(&$$, src_next, $2); } lns : R_LN | lns R_LN %% static void warning(const char *s) { fprintf(stderr, "%s in %d\n", s, linenumber); } int yyerror(const char *s) { warning(s); exit(1); } void put8(void *ptr, size_t ofs, u_int32_t val) { *((u_int8_t *)ptr + ofs) = val; } void put16(void *ptr, size_t ofs, u_int32_t val) { u_int16_t oval = htons(val); memcpy((u_int16_t *)ptr + ofs, &oval, 2); } void put32(void *ptr, size_t ofs, u_int32_t val) { u_int32_t oval = htonl(val); memcpy((u_int32_t *)ptr + ofs, &oval, 4); } static void alloc_table(void) { linear_zone_t *p; size_t i; uint32_t val = 0; i = rowcol_len; p = &rowcol[--i]; table_size = p->width; while (i > 0) { p = &rowcol[--i]; table_size *= p->width; } table = (void *)malloc(table_size * dst_unit_bits / 8); if (table == NULL) { perror("malloc"); exit(1); } switch (oob_mode) { case _CITRUS_MAPPER_STD_OOB_NONIDENTICAL: val = dst_invalid; break; case _CITRUS_MAPPER_STD_OOB_ILSEQ: val = dst_ilseq; break; default: break; } for (i = 0; i < table_size; i++) (*putfunc)(table, i, val); } static void setup_map(void) { if ((done_flag & DF_SRC_ZONE)==0) { fprintf(stderr, "SRC_ZONE is mandatory.\n"); exit(1); } if ((done_flag & DF_DST_UNIT_BITS)==0) { fprintf(stderr, "DST_UNIT_BITS is mandatory.\n"); exit(1); } if ((done_flag & DF_DST_INVALID) == 0) dst_invalid = 0xFFFFFFFF; if ((done_flag & DF_DST_ILSEQ) == 0) dst_ilseq = 0xFFFFFFFE; if ((done_flag & DF_OOB_MODE) == 0) oob_mode = _CITRUS_MAPPER_STD_OOB_NONIDENTICAL; alloc_table(); } static void create_rowcol_info(struct _region *r) { void *ptr; size_t i, len, ofs; ofs = 0; ptr = malloc(_CITRUS_MAPPER_STD_ROWCOL_INFO_SIZE); if (ptr == NULL) err(EXIT_FAILURE, "malloc"); put32(ptr, ofs, rowcol_bits); ofs++; put32(ptr, ofs, dst_invalid); ofs++; /* XXX: keep backward compatibility */ switch (rowcol_len) { case 1: put32(ptr, ofs, 0); ofs++; put32(ptr, ofs, 0); ofs++; /*FALLTHROUGH*/ case 2: len = 0; break; default: len = rowcol_len; } for (i = 0; i < rowcol_len; ++i) { put32(ptr, ofs, rowcol[i].begin); ofs++; put32(ptr, ofs, rowcol[i].end); ofs++; } put32(ptr, ofs, dst_unit_bits); ofs++; put32(ptr, ofs, len); ofs++; _region_init(r, ptr, ofs * 4); } static void create_rowcol_ext_ilseq_info(struct _region *r) { void *ptr; size_t ofs; ofs = 0; ptr = malloc(_CITRUS_MAPPER_STD_ROWCOL_EXT_ILSEQ_SIZE); if (ptr == NULL) err(EXIT_FAILURE, "malloc"); put32(ptr, ofs, oob_mode); ofs++; put32(ptr, ofs, dst_ilseq); ofs++; _region_init(r, ptr, _CITRUS_MAPPER_STD_ROWCOL_EXT_ILSEQ_SIZE); } #define CHKERR(ret, func, a) \ do { \ ret = func a; \ if (ret) \ errx(EXIT_FAILURE, "%s: %s", #func, strerror(ret)); \ } while (/*CONSTCOND*/0) static void dump_file(void) { struct _db_factory *df; struct _region data; void *serialized; FILE *fp; size_t size; int ret; /* * build database */ CHKERR(ret, _db_factory_create, (&df, _db_hash_std, NULL)); /* store type */ CHKERR(ret, _db_factory_addstr_by_s, (df, _CITRUS_MAPPER_STD_SYM_TYPE, _CITRUS_MAPPER_STD_TYPE_ROWCOL)); /* store info */ create_rowcol_info(&data); CHKERR(ret, _db_factory_add_by_s, (df, _CITRUS_MAPPER_STD_SYM_INFO, &data, 1)); /* ilseq extension */ create_rowcol_ext_ilseq_info(&data); CHKERR(ret, _db_factory_add_by_s, (df, _CITRUS_MAPPER_STD_SYM_ROWCOL_EXT_ILSEQ, &data, 1)); /* store table */ _region_init(&data, table, table_size*dst_unit_bits/8); CHKERR(ret, _db_factory_add_by_s, (df, _CITRUS_MAPPER_STD_SYM_TABLE, &data, 1)); /* * dump database to file */ fp = output ? fopen(output, "wb") : stdout; if (fp == NULL) { perror("fopen"); exit(1); } /* dump database body */ size = _db_factory_calc_size(df); serialized = malloc(size); _region_init(&data, serialized, size); CHKERR(ret, _db_factory_serialize, (df, _CITRUS_MAPPER_STD_MAGIC, &data)); if (fwrite(serialized, size, 1, fp) != 1) err(EXIT_FAILURE, "fwrite"); fclose(fp); } static void /*ARGSUSED*/ set_type(int type) { if (done_flag & DF_TYPE) { warning("TYPE is duplicated. ignored this one"); return; } map_type = type; done_flag |= DF_TYPE; } static void /*ARGSUSED*/ set_name(char *str) { if (done_flag & DF_NAME) { warning("NAME is duplicated. ignored this one"); return; } map_name = str; done_flag |= DF_NAME; } static void set_src_zone(u_int32_t val) { linear_zone_t *p; size_t i; if (done_flag & DF_SRC_ZONE) { warning("SRC_ZONE is duplicated. ignored this one"); return; } rowcol_bits = val; /* sanity check */ switch (rowcol_bits) { case 8: case 16: case 32: if (rowcol_len <= 32 / rowcol_bits) break; /*FALLTHROUGH*/ default: goto bad; } rowcol_mask = 1u << (rowcol_bits - 1); rowcol_mask |= rowcol_mask - 1; for (i = 0; i < rowcol_len; ++i) { p = &rowcol[i]; if (p->end > rowcol_mask) goto bad; } done_flag |= DF_SRC_ZONE; return; bad: yyerror("Illegal argument for SRC_ZONE"); } static void set_dst_invalid(u_int32_t val) { if (done_flag & DF_DST_INVALID) { warning("DST_INVALID is duplicated. ignored this one"); return; } dst_invalid = val; done_flag |= DF_DST_INVALID; } static void set_dst_ilseq(u_int32_t val) { if (done_flag & DF_DST_ILSEQ) { warning("DST_ILSEQ is duplicated. ignored this one"); return; } dst_ilseq = val; done_flag |= DF_DST_ILSEQ; } static void set_oob_mode(u_int32_t val) { if (done_flag & DF_OOB_MODE) { warning("OOB_MODE is duplicated. ignored this one"); return; } oob_mode = val; done_flag |= DF_OOB_MODE; } static void set_dst_unit_bits(u_int32_t val) { if (done_flag & DF_DST_UNIT_BITS) { warning("DST_UNIT_BITS is duplicated. ignored this one"); return; } switch (val) { case 8: putfunc = &put8; dst_unit_bits = val; break; case 16: putfunc = &put16; dst_unit_bits = val; break; case 32: putfunc = &put32; dst_unit_bits = val; break; default: yyerror("Illegal argument for DST_UNIT_BITS"); } done_flag |= DF_DST_UNIT_BITS; } static int check_src(u_int32_t begin, u_int32_t end) { linear_zone_t *p; size_t i; u_int32_t m, n; if (begin > end) return (1); if (begin < end) { m = begin & ~rowcol_mask; n = end & ~rowcol_mask; if (m != n) return (1); } for (i = rowcol_len * rowcol_bits, p = &rowcol[0]; i > 0; ++p) { i -= rowcol_bits; m = (begin >> i) & rowcol_mask; if (m < p->begin || m > p->end) return (1); } if (begin < end) { n = end & rowcol_mask; --p; if (n < p->begin || n > p->end) return (1); } return (0); } static void store(const linear_zone_t *lz, u_int32_t dst, int inc) { linear_zone_t *p; size_t i, ofs; u_int32_t n; ofs = 0; for (i = rowcol_len * rowcol_bits, p = &rowcol[0]; i > 0; ++p) { i -= rowcol_bits; n = ((lz->begin >> i) & rowcol_mask) - p->begin; ofs = (ofs * p->width) + n; } n = lz->width; while (n-- > 0) { (*putfunc)(table, ofs++, dst); if (inc) dst++; } } static void set_range(u_int32_t begin, u_int32_t end) { linear_zone_t *p; if (rowcol_len >= _CITRUS_MAPPER_STD_ROWCOL_MAX) goto bad; p = &rowcol[rowcol_len++]; if (begin > end) goto bad; p->begin = begin, p->end = end; p->width = end - begin + 1; return; bad: yyerror("Illegal argument for SRC_ZONE"); } static void set_src(linear_zone_t *lz, u_int32_t begin, u_int32_t end) { if (check_src(begin, end) != 0) yyerror("illegal zone"); lz->begin = begin, lz->end = end; lz->width = end - begin + 1; src_next = end + 1; } static void do_mkdb(FILE *in) { FILE *out; int ret; /* dump DB to file */ out = output ? fopen(output, "wb") : stdout; if (out == NULL) err(EXIT_FAILURE, "fopen"); ret = _lookup_factory_convert(out, in); fclose(out); if (ret && output) unlink(output); /* dump failure */ } static void do_mkpv(FILE *in) { FILE *out; int ret; /* dump pivot to file */ out = output ? fopen(output, "wb") : stdout; if (out == NULL) err(EXIT_FAILURE, "fopen"); ret = _pivot_factory_convert(out, in); fclose(out); if (ret && output) unlink(output); /* dump failure */ if (ret) errx(EXIT_FAILURE, "%s\n", strerror(ret)); } static void usage(void) { fprintf(stderr, "Usage: %s [-d] [-m|-p] [-o outfile] [infile]\n", getprogname()); exit(EXIT_FAILURE); } int main(int argc, char **argv) { FILE *in = NULL; int ch, mkdb = 0, mkpv = 0; while ((ch = getopt(argc, argv, "do:mp")) != EOF) { switch (ch) { case 'd': debug = 1; break; case 'o': output = strdup(optarg); break; case 'm': mkdb = 1; break; case 'p': mkpv = 1; break; default: usage(); } } argc -= optind; argv += optind; switch (argc) { case 0: in = stdin; break; case 1: in = fopen(argv[0], "r"); if (!in) err(EXIT_FAILURE, "%s", argv[0]); break; default: usage(); } if (mkdb) do_mkdb(in); else if (mkpv) do_mkpv(in); else { yyin = in; yyparse(); } return (0); } diff --git a/usr.bin/mkesdb/yacc.y b/usr.bin/mkesdb/yacc.y index 9f1781f31b59..9758fa9dd4c4 100644 --- a/usr.bin/mkesdb/yacc.y +++ b/usr.bin/mkesdb/yacc.y @@ -1,333 +1,332 @@ /* $NetBSD: yacc.y,v 1.4 2005/06/02 02:09:25 lukem Exp $ */ %{ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c)2003 Citrus Project, * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include "citrus_namespace.h" #include "citrus_types.h" #include "citrus_region.h" #include "citrus_esdb_file.h" #include "citrus_db_hash.h" #include "citrus_db_factory.h" #include "citrus_lookup_factory.h" #include "ldef.h" extern FILE *yyin; static struct named_csid_list named_csids; static char *encoding, *name, *output = NULL, *variable; static u_int32_t invalid; static int debug = 0, num_csids = 0, use_invalid = 0; static void dump_file(void); static void register_named_csid(char *, u_int32_t); static void set_invalid(u_int32_t); static void set_prop_string(const char *, char **, char **); %} %union { u_int32_t i_value; char *s_value; } %token R_NAME R_ENCODING R_VARIABLE R_DEFCSID R_INVALID %token R_LN %token L_IMM %token L_STRING %% file : property { dump_file(); } property : /* empty */ | property R_LN | property name R_LN | property encoding R_LN | property variable R_LN | property defcsid R_LN | property invalid R_LN name : R_NAME L_STRING { set_prop_string("NAME", &name, &$2); } encoding : R_ENCODING L_STRING { set_prop_string("ENCODING", &encoding, &$2); } variable : R_VARIABLE L_STRING { set_prop_string("VARIABLE", &variable, &$2); } defcsid : R_DEFCSID L_STRING L_IMM { register_named_csid($2, $3); $2 = NULL; } invalid : R_INVALID L_IMM { set_invalid($2); } %% int yyerror(const char *s) { fprintf(stderr, "%s in %d\n", s, linenumber); return (0); } #define CHKERR(ret, func, a) \ do { \ ret = func a; \ if (ret) \ errx(EXIT_FAILURE, "%s: %s", #func, strerror(ret)); \ } while (/*CONSTCOND*/0) static void dump_file(void) { struct _db_factory *df; struct _region data; struct named_csid *csid; FILE *fp; char buf[100]; void *serialized; size_t size; int i, ret; ret = 0; if (!name) { fprintf(stderr, "NAME is mandatory.\n"); ret = 1; } if (!encoding) { fprintf(stderr, "ENCODING is mandatory.\n"); ret = 1; } if (ret) exit(1); /* * build database */ CHKERR(ret, _db_factory_create, (&df, _db_hash_std, NULL)); /* store version */ CHKERR(ret, _db_factory_add32_by_s, (df, _CITRUS_ESDB_SYM_VERSION, _CITRUS_ESDB_VERSION)); /* store encoding */ CHKERR(ret, _db_factory_addstr_by_s, (df, _CITRUS_ESDB_SYM_ENCODING, encoding)); /* store variable */ if (variable) CHKERR(ret, _db_factory_addstr_by_s, (df, _CITRUS_ESDB_SYM_VARIABLE, variable)); /* store invalid */ if (use_invalid) CHKERR(ret, _db_factory_add32_by_s, (df, _CITRUS_ESDB_SYM_INVALID, invalid)); /* store num of charsets */ CHKERR(ret, _db_factory_add32_by_s, (df, _CITRUS_ESDB_SYM_NUM_CHARSETS, num_csids)); i = 0; STAILQ_FOREACH(csid, &named_csids, ci_entry) { snprintf(buf, sizeof(buf), _CITRUS_ESDB_SYM_CSNAME_PREFIX "%d", i); CHKERR(ret, _db_factory_addstr_by_s, (df, buf, csid->ci_symbol)); snprintf(buf, sizeof(buf), _CITRUS_ESDB_SYM_CSID_PREFIX "%d", i); CHKERR(ret, _db_factory_add32_by_s, (df, buf, csid->ci_csid)); i++; } /* * dump database to file */ fp = output ? fopen(output, "wb") : stdout; if (fp == NULL) { perror("fopen"); exit(1); } /* dump database body */ size = _db_factory_calc_size(df); serialized = malloc(size); _region_init(&data, serialized, size); CHKERR(ret, _db_factory_serialize, (df, _CITRUS_ESDB_MAGIC, &data)); if (fwrite(serialized, size, 1, fp) != 1) err(EXIT_FAILURE, "fwrite"); fclose(fp); } static void set_prop_string(const char *res, char **store, char **data) { char buf[256]; if (*store) { snprintf(buf, sizeof(buf), "%s is duplicated. ignored the one", res); yyerror(buf); return; } *store = *data; *data = NULL; } static void set_invalid(u_int32_t inv) { invalid = inv; use_invalid = 1; } static void register_named_csid(char *sym, u_int32_t val) { struct named_csid *csid; STAILQ_FOREACH(csid, &named_csids, ci_entry) { if (strcmp(csid->ci_symbol, sym) == 0) { yyerror("multiply defined CSID"); exit(1); } } csid = malloc(sizeof(*csid)); if (csid == NULL) { perror("malloc"); exit(1); } csid->ci_symbol = sym; csid->ci_csid = val; STAILQ_INSERT_TAIL(&named_csids, csid, ci_entry); num_csids++; } static void do_mkdb(FILE *in) { FILE *out; int ret; /* dump DB to file */ out = output ? fopen(output, "wb") : stdout; if (out == NULL) err(EXIT_FAILURE, "fopen"); ret = _lookup_factory_convert(out, in); fclose(out); if (ret && output) unlink(output); /* dump failure */ if (ret) errx(EXIT_FAILURE, "%s\n", strerror(ret)); } static void usage(void) { errx(EXIT_FAILURE, "usage:\n" "\t%s [-d] [-o outfile] [infile]\n" "\t%s -m [-d] [-o outfile] [infile]", getprogname(), getprogname()); } int main(int argc, char **argv) { FILE *in = NULL; int ch, mkdb = 0; while ((ch = getopt(argc, argv, "do:m")) != EOF) { switch (ch) { case 'd': debug = 1; break; case 'o': output = strdup(optarg); break; case 'm': mkdb = 1; break; default: usage(); } } argc -= optind; argv += optind; switch (argc) { case 0: in = stdin; break; case 1: in = fopen(argv[0], "r"); if (!in) err(EXIT_FAILURE, "%s", argv[0]); break; default: usage(); } if (mkdb) do_mkdb(in); else { STAILQ_INIT(&named_csids); yyin = in; yyparse(); } return (0); } diff --git a/usr.bin/mkimg/mkimg.c b/usr.bin/mkimg/mkimg.c index bcf500cd675f..541d534f967f 100644 --- a/usr.bin/mkimg/mkimg.c +++ b/usr.bin/mkimg/mkimg.c @@ -1,733 +1,732 @@ /*- * Copyright (c) 2013,2014 Juniper Networks, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "image.h" #include "format.h" #include "mkimg.h" #include "scheme.h" #define LONGOPT_FORMATS 0x01000001 #define LONGOPT_SCHEMES 0x01000002 #define LONGOPT_VERSION 0x01000003 #define LONGOPT_CAPACITY 0x01000004 static struct option longopts[] = { { "formats", no_argument, NULL, LONGOPT_FORMATS }, { "schemes", no_argument, NULL, LONGOPT_SCHEMES }, { "version", no_argument, NULL, LONGOPT_VERSION }, { "capacity", required_argument, NULL, LONGOPT_CAPACITY }, { NULL, 0, NULL, 0 } }; static uint64_t min_capacity = 0; static uint64_t max_capacity = 0; struct partlisthead partlist = TAILQ_HEAD_INITIALIZER(partlist); u_int nparts = 0; u_int unit_testing; u_int verbose; u_int ncyls = 0; u_int nheads = 1; u_int nsecs = 1; u_int secsz = 512; u_int blksz = 0; uint32_t active_partition = 0; static void print_formats(int usage) { struct mkimg_format *f; const char *sep; if (usage) { fprintf(stderr, " formats:\n"); f = NULL; while ((f = format_iterate(f)) != NULL) { fprintf(stderr, "\t%s\t- %s\n", f->name, f->description); } } else { sep = ""; f = NULL; while ((f = format_iterate(f)) != NULL) { printf("%s%s", sep, f->name); sep = " "; } putchar('\n'); } } static void print_schemes(int usage) { struct mkimg_scheme *s; const char *sep; if (usage) { fprintf(stderr, " schemes:\n"); s = NULL; while ((s = scheme_iterate(s)) != NULL) { fprintf(stderr, "\t%s\t- %s\n", s->name, s->description); } } else { sep = ""; s = NULL; while ((s = scheme_iterate(s)) != NULL) { printf("%s%s", sep, s->name); sep = " "; } putchar('\n'); } } static void print_version(void) { u_int width; #ifdef __LP64__ width = 64; #else width = 32; #endif printf("mkimg %u (%u-bit)\n", MKIMG_VERSION, width); } static void usage(const char *why) { warnx("error: %s", why); fputc('\n', stderr); fprintf(stderr, "usage: %s \n", getprogname()); fprintf(stderr, " options:\n"); fprintf(stderr, "\t--formats\t- list image formats\n"); fprintf(stderr, "\t--schemes\t- list partition schemes\n"); fprintf(stderr, "\t--version\t- show version information\n"); fputc('\n', stderr); fprintf(stderr, "\t-a \t- mark num'th partition as active\n"); fprintf(stderr, "\t-b \t- file containing boot code\n"); fprintf(stderr, "\t-c \t- minimum capacity (in bytes) of the disk\n"); fprintf(stderr, "\t-C \t- maximum capacity (in bytes) of the disk\n"); fprintf(stderr, "\t-f \n"); fprintf(stderr, "\t-o \t- file to write image into\n"); fprintf(stderr, "\t-p \n"); fprintf(stderr, "\t-s \n"); fprintf(stderr, "\t-v\t\t- increase verbosity\n"); fprintf(stderr, "\t-y\t\t- [developers] enable unit test\n"); fprintf(stderr, "\t-H \t- number of heads to simulate\n"); fprintf(stderr, "\t-P \t- physical sector size\n"); fprintf(stderr, "\t-S \t- logical sector size\n"); fprintf(stderr, "\t-T \t- number of tracks to simulate\n"); fputc('\n', stderr); print_formats(1); fputc('\n', stderr); print_schemes(1); fputc('\n', stderr); fprintf(stderr, " partition specification:\n"); fprintf(stderr, "\t[/]::[:[+]]\t- " "empty partition of given size and\n\t\t\t\t\t" " optional relative or absolute offset\n"); fprintf(stderr, "\t[/]:=[:[+]offset]\t- partition " "content and size are\n\t\t\t\t\t" " determined by the named file and\n" "\t\t\t\t\t optional relative or absolute offset\n"); fprintf(stderr, "\t[/]:-\t\t- partition content and size " "are taken\n\t\t\t\t\t from the output of the command to run\n"); fprintf(stderr, "\t-\t\t\t\t- unused partition entry\n"); fprintf(stderr, "\t where:\n"); fprintf(stderr, "\t\t\t- scheme neutral partition type\n"); fprintf(stderr, "\t\t\t- optional scheme-dependent partition " "label\n"); exit(EX_USAGE); } static int parse_uint32(uint32_t *valp, uint32_t min, uint32_t max, const char *arg) { uint64_t val; if (expand_number(arg, &val) == -1) return (errno); if (val > UINT_MAX || val < (uint64_t)min || val > (uint64_t)max) return (EINVAL); *valp = (uint32_t)val; return (0); } static int parse_uint64(uint64_t *valp, uint64_t min, uint64_t max, const char *arg) { uint64_t val; if (expand_number(arg, &val) == -1) return (errno); if (val < min || val > max) return (EINVAL); *valp = val; return (0); } static int pwr_of_two(u_int nr) { return (((nr & (nr - 1)) == 0) ? 1 : 0); } /* * A partition specification has the following format: * ':' * where: * type the partition type alias * kind the interpretation of the contents specification * ':' contents holds the size of an empty partition * '=' contents holds the name of a file to read * '-' contents holds a command to run; the output of * which is the contents of the partition. * contents the specification of a partition's contents * * A specification that is a single dash indicates an unused partition * entry. */ static int parse_part(const char *spec) { struct part *part; char *sep; size_t len; int error; if (strcmp(spec, "-") == 0) { nparts++; return (0); } part = calloc(1, sizeof(struct part)); if (part == NULL) return (ENOMEM); sep = strchr(spec, ':'); if (sep == NULL) { error = EINVAL; goto errout; } len = sep - spec + 1; if (len < 2) { error = EINVAL; goto errout; } part->alias = malloc(len); if (part->alias == NULL) { error = ENOMEM; goto errout; } strlcpy(part->alias, spec, len); spec = sep + 1; switch (*spec) { case ':': part->kind = PART_KIND_SIZE; break; case '=': part->kind = PART_KIND_FILE; break; case '-': part->kind = PART_KIND_PIPE; break; default: error = EINVAL; goto errout; } spec++; part->contents = strdup(spec); if (part->contents == NULL) { error = ENOMEM; goto errout; } spec = part->alias; sep = strchr(spec, '/'); if (sep != NULL) { *sep++ = '\0'; if (strlen(part->alias) == 0 || strlen(sep) == 0) { error = EINVAL; goto errout; } part->label = strdup(sep); if (part->label == NULL) { error = ENOMEM; goto errout; } } part->index = nparts; TAILQ_INSERT_TAIL(&partlist, part, link); nparts++; return (0); errout: if (part->alias != NULL) free(part->alias); free(part); return (error); } #if defined(SPARSE_WRITE) ssize_t sparse_write(int fd, const void *ptr, size_t sz) { const char *buf, *p; off_t ofs; size_t len; ssize_t wr, wrsz; buf = ptr; wrsz = 0; p = memchr(buf, 0, sz); while (sz > 0) { len = (p != NULL) ? (size_t)(p - buf) : sz; if (len > 0) { len = (len + secsz - 1) & ~(secsz - 1); if (len > sz) len = sz; wr = write(fd, buf, len); if (wr < 0) return (-1); } else { while (len < sz && *p++ == '\0') len++; if (len < sz) len &= ~(secsz - 1); if (len == 0) continue; ofs = lseek(fd, len, SEEK_CUR); if (ofs < 0) return (-1); wr = len; } buf += wr; sz -= wr; wrsz += wr; p = memchr(buf, 0, sz); } return (wrsz); } #endif /* SPARSE_WRITE */ void mkimg_chs(lba_t lba, u_int maxcyl, u_int *cylp, u_int *hdp, u_int *secp) { u_int hd, sec; *cylp = *hdp = *secp = ~0U; if (nsecs == 1 || nheads == 1) return; sec = lba % nsecs + 1; lba /= nsecs; hd = lba % nheads; lba /= nheads; if (lba > maxcyl) return; *cylp = lba; *hdp = hd; *secp = sec; } static int capacity_resize(lba_t end) { lba_t min_capsz, max_capsz; min_capsz = (min_capacity + secsz - 1) / secsz; max_capsz = (max_capacity + secsz - 1) / secsz; if (max_capsz != 0 && end > max_capsz) return (ENOSPC); if (end >= min_capsz) return (0); return (image_set_size(min_capsz)); } static void mkimg_validate(void) { struct part *part, *part2; lba_t start, end, start2, end2; int i, j; i = 0; TAILQ_FOREACH(part, &partlist, link) { start = part->block; end = part->block + part->size; j = i + 1; part2 = TAILQ_NEXT(part, link); if (part2 == NULL) break; TAILQ_FOREACH_FROM(part2, &partlist, link) { start2 = part2->block; end2 = part2->block + part2->size; if ((start >= start2 && start < end2) || (end > start2 && end <= end2)) { errx(1, "partition %d overlaps partition %d", i, j); } j++; } i++; } } static void mkimg(void) { FILE *fp; struct part *part; lba_t block, blkoffset; uint64_t bytesize, byteoffset; char *size, *offset; bool abs_offset; int error, fd; /* First check partition information */ TAILQ_FOREACH(part, &partlist, link) { error = scheme_check_part(part); if (error) errc(EX_DATAERR, error, "partition %d", part->index+1); } block = scheme_metadata(SCHEME_META_IMG_START, 0); abs_offset = false; TAILQ_FOREACH(part, &partlist, link) { byteoffset = blkoffset = 0; abs_offset = false; /* Look for an offset. Set size too if we can. */ switch (part->kind) { case PART_KIND_SIZE: case PART_KIND_FILE: offset = part->contents; size = strsep(&offset, ":"); if (part->kind == PART_KIND_SIZE && expand_number(size, &bytesize) == -1) error = errno; if (offset != NULL) { if (*offset != '+') abs_offset = true; else offset++; if (expand_number(offset, &byteoffset) == -1) error = errno; } break; } /* Work out exactly where the partition starts. */ blkoffset = (byteoffset + secsz - 1) / secsz; if (abs_offset) block = scheme_metadata(SCHEME_META_PART_ABSOLUTE, blkoffset); else block = scheme_metadata(SCHEME_META_PART_BEFORE, block + blkoffset); part->block = block; if (verbose) fprintf(stderr, "partition %d: starting block %llu " "... ", part->index + 1, (long long)part->block); /* Pull in partition contents, set size if we haven't yet. */ switch (part->kind) { case PART_KIND_FILE: fd = open(part->contents, O_RDONLY, 0); if (fd != -1) { error = image_copyin(block, fd, &bytesize); close(fd); } else error = errno; break; case PART_KIND_PIPE: fp = popen(part->contents, "r"); if (fp != NULL) { fd = fileno(fp); error = image_copyin(block, fd, &bytesize); pclose(fp); } else error = errno; break; } if (error) errc(EX_IOERR, error, "partition %d", part->index + 1); part->size = (bytesize + secsz - 1) / secsz; if (verbose) { bytesize = part->size * secsz; fprintf(stderr, "size %llu bytes (%llu blocks)\n", (long long)bytesize, (long long)part->size); if (abs_offset) { fprintf(stderr, " location %llu bytes (%llu blocks)\n", (long long)byteoffset, (long long)blkoffset); } else if (blkoffset > 0) { fprintf(stderr, " offset %llu bytes (%llu blocks)\n", (long long)byteoffset, (long long)blkoffset); } } block = scheme_metadata(SCHEME_META_PART_AFTER, part->block + part->size); } mkimg_validate(); block = scheme_metadata(SCHEME_META_IMG_END, block); error = image_set_size(block); if (!error) { error = capacity_resize(block); block = image_get_size(); } if (!error) { error = format_resize(block); block = image_get_size(); } if (error) errc(EX_IOERR, error, "image sizing"); ncyls = block / (nsecs * nheads); error = scheme_write(block); if (error) errc(EX_IOERR, error, "writing metadata"); } int main(int argc, char *argv[]) { int bcfd, outfd; int c, error; bcfd = -1; outfd = 1; /* Write to stdout by default */ while ((c = getopt_long(argc, argv, "a:b:c:C:f:o:p:s:vyH:P:S:T:", longopts, NULL)) != -1) { switch (c) { case 'a': /* ACTIVE PARTITION, if supported */ error = parse_uint32(&active_partition, 1, 100, optarg); if (error) errc(EX_DATAERR, error, "Partition ordinal"); break; case 'b': /* BOOT CODE */ if (bcfd != -1) usage("multiple bootcode given"); bcfd = open(optarg, O_RDONLY, 0); if (bcfd == -1) err(EX_UNAVAILABLE, "%s", optarg); break; case 'c': /* MINIMUM CAPACITY */ error = parse_uint64(&min_capacity, 1, INT64_MAX, optarg); if (error) errc(EX_DATAERR, error, "minimum capacity in bytes"); break; case 'C': /* MAXIMUM CAPACITY */ error = parse_uint64(&max_capacity, 1, INT64_MAX, optarg); if (error) errc(EX_DATAERR, error, "maximum capacity in bytes"); break; case 'f': /* OUTPUT FORMAT */ if (format_selected() != NULL) usage("multiple formats given"); error = format_select(optarg); if (error) errc(EX_DATAERR, error, "format"); break; case 'o': /* OUTPUT FILE */ if (outfd != 1) usage("multiple output files given"); outfd = open(optarg, O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); if (outfd == -1) err(EX_CANTCREAT, "%s", optarg); break; case 'p': /* PARTITION */ error = parse_part(optarg); if (error) errc(EX_DATAERR, error, "partition"); break; case 's': /* SCHEME */ if (scheme_selected() != NULL) usage("multiple schemes given"); error = scheme_select(optarg); if (error) errc(EX_DATAERR, error, "scheme"); break; case 'y': unit_testing++; break; case 'v': verbose++; break; case 'H': /* GEOMETRY: HEADS */ error = parse_uint32(&nheads, 1, 255, optarg); if (error) errc(EX_DATAERR, error, "number of heads"); break; case 'P': /* GEOMETRY: PHYSICAL SECTOR SIZE */ error = parse_uint32(&blksz, 512, INT_MAX+1U, optarg); if (error == 0 && !pwr_of_two(blksz)) error = EINVAL; if (error) errc(EX_DATAERR, error, "physical sector size"); break; case 'S': /* GEOMETRY: LOGICAL SECTOR SIZE */ error = parse_uint32(&secsz, 512, INT_MAX+1U, optarg); if (error == 0 && !pwr_of_two(secsz)) error = EINVAL; if (error) errc(EX_DATAERR, error, "logical sector size"); break; case 'T': /* GEOMETRY: TRACK SIZE */ error = parse_uint32(&nsecs, 1, 63, optarg); if (error) errc(EX_DATAERR, error, "track size"); break; case LONGOPT_FORMATS: print_formats(0); exit(EX_OK); /*NOTREACHED*/ case LONGOPT_SCHEMES: print_schemes(0); exit(EX_OK); /*NOTREACHED*/ case LONGOPT_VERSION: print_version(); exit(EX_OK); /*NOTREACHED*/ case LONGOPT_CAPACITY: error = parse_uint64(&min_capacity, 1, INT64_MAX, optarg); if (error) errc(EX_DATAERR, error, "capacity in bytes"); max_capacity = min_capacity; break; default: usage("unknown option"); } } if (argc > optind) usage("trailing arguments"); if (scheme_selected() == NULL && nparts > 0) usage("no scheme"); if (nparts == 0 && min_capacity == 0) usage("no partitions"); if (max_capacity != 0 && min_capacity > max_capacity) usage("minimum capacity cannot be larger than the maximum one"); if (secsz > blksz) { if (blksz != 0) errx(EX_DATAERR, "the physical block size cannot " "be smaller than the sector size"); blksz = secsz; } if (secsz > scheme_max_secsz()) errx(EX_DATAERR, "maximum sector size supported is %u; " "size specified is %u", scheme_max_secsz(), secsz); if (nparts > scheme_max_parts()) errx(EX_DATAERR, "%d partitions supported; %d given", scheme_max_parts(), nparts); if (format_selected() == NULL) format_select("raw"); if (bcfd != -1) { error = scheme_bootcode(bcfd); close(bcfd); if (error) errc(EX_DATAERR, error, "boot code"); } if (verbose) { fprintf(stderr, "Logical sector size: %u\n", secsz); fprintf(stderr, "Physical block size: %u\n", blksz); fprintf(stderr, "Sectors per track: %u\n", nsecs); fprintf(stderr, "Number of heads: %u\n", nheads); fputc('\n', stderr); if (scheme_selected()) fprintf(stderr, "Partitioning scheme: %s\n", scheme_selected()->name); fprintf(stderr, "Output file format: %s\n", format_selected()->name); fputc('\n', stderr); } error = image_init(); if (error) errc(EX_OSERR, error, "cannot initialize"); mkimg(); if (verbose) { fputc('\n', stderr); fprintf(stderr, "Number of cylinders: %u\n", ncyls); } error = format_write(outfd); if (error) errc(EX_IOERR, error, "writing image"); return (0); } diff --git a/usr.bin/mkuzip/mkuz_conveyor.c b/usr.bin/mkuzip/mkuz_conveyor.c index b355be419907..0dee2342c0d1 100644 --- a/usr.bin/mkuzip/mkuz_conveyor.c +++ b/usr.bin/mkuzip/mkuz_conveyor.c @@ -1,128 +1,127 @@ /* * Copyright (c) 2004-2016 Maxim Sobolev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #if defined(MKUZ_DEBUG) # include #endif #include "mkuz_conveyor.h" #include "mkuz_cfg.h" #include "mkuzip.h" #include "mkuz_blk.h" #include "mkuz_format.h" #include "mkuz_fqueue.h" #include "mkuz_blk_chain.h" static void compute_digest(struct mkuz_blk *); struct cw_args { struct mkuz_conveyor *cvp; struct mkuz_cfg *cfp; }; static void * cworker(void *p) { struct cw_args *cwp; struct mkuz_cfg *cfp; struct mkuz_blk *oblk, *iblk; struct mkuz_conveyor *cvp; void *c_ctx; cwp = (struct cw_args *)p; cfp = cwp->cfp; cvp = cwp->cvp; free(cwp); c_ctx = cfp->handler->f_init(&cfp->comp_level); for (;;) { iblk = mkuz_fqueue_deq(cvp->wrk_queue); if (iblk == MKUZ_BLK_EOF) { /* Let other threads to see the EOF block */ mkuz_fqueue_enq(cvp->wrk_queue, iblk); break; } if (cfp->no_zcomp == 0 && mkuz_memvcmp(iblk->data, '\0', iblk->info.len) != 0) { /* All zeroes block */ oblk = mkuz_blk_ctor(0); } else { oblk = mkuz_blk_ctor(cfp->cbound_blksz); cfp->handler->f_compress(c_ctx, iblk, oblk); if (cfp->en_dedup != 0) { compute_digest(oblk); } } oblk->info.blkno = iblk->info.blkno; mkuz_fqueue_enq(cvp->results, oblk); free(iblk); } return (NULL); } static void compute_digest(struct mkuz_blk *bp) { MD5_CTX mcontext; MD5Init(&mcontext); MD5Update(&mcontext, bp->data, bp->info.len); MD5Final(bp->info.digest, &mcontext); } struct mkuz_conveyor * mkuz_conveyor_ctor(struct mkuz_cfg *cfp) { struct mkuz_conveyor *cp; struct cw_args *cwp; int i, r; cp = mkuz_safe_zmalloc(sizeof(struct mkuz_conveyor) + (sizeof(pthread_t) * cfp->nworkers)); cp->wrk_queue = mkuz_fqueue_ctor(1); cp->results = mkuz_fqueue_ctor(1); for (i = 0; i < cfp->nworkers; i++) { cwp = mkuz_safe_zmalloc(sizeof(struct cw_args)); cwp->cfp = cfp; cwp->cvp = cp; r = pthread_create(&cp->wthreads[i], NULL, cworker, (void *)cwp); if (r != 0) { errx(1, "mkuz_conveyor_ctor: pthread_create() failed"); /* Not reached */ } } return (cp); } diff --git a/usr.bin/mkuzip/mkuz_lzma.c b/usr.bin/mkuzip/mkuz_lzma.c index 1c2ccf6a5045..7c713e25aed3 100644 --- a/usr.bin/mkuzip/mkuz_lzma.c +++ b/usr.bin/mkuzip/mkuz_lzma.c @@ -1,110 +1,109 @@ /* * Copyright (c) 2004-2016 Maxim Sobolev * Copyright (c) 2011 Aleksandr Rybalko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include "mkuzip.h" #include "mkuz_blk.h" #include "mkuz_lzma.h" struct mkuz_lzma { lzma_filter filters[2]; lzma_options_lzma opt_lzma; lzma_stream strm; }; size_t mkuz_lzma_cbound(size_t blksz) { return (lzma_stream_buffer_bound(blksz)); } void * mkuz_lzma_init(int *comp_level) { struct mkuz_lzma *ulp; if (*comp_level == USE_DEFAULT_LEVEL) *comp_level = LZMA_PRESET_DEFAULT; if (*comp_level < 0 || *comp_level > 9) errx(1, "provided compression level %d is invalid", *comp_level); /* Not reached */ ulp = mkuz_safe_zmalloc(sizeof(struct mkuz_lzma)); /* Init lzma encoder */ ulp->strm = (lzma_stream)LZMA_STREAM_INIT; if (lzma_lzma_preset(&ulp->opt_lzma, *comp_level)) errx(1, "Error loading LZMA preset"); ulp->filters[0].id = LZMA_FILTER_LZMA2; ulp->filters[0].options = &ulp->opt_lzma; ulp->filters[1].id = LZMA_VLI_UNKNOWN; return (void *)ulp; } void mkuz_lzma_compress(void *p, const struct mkuz_blk *iblk, struct mkuz_blk *oblk) { lzma_ret ret; struct mkuz_lzma *ulp; ulp = (struct mkuz_lzma *)p; ret = lzma_stream_encoder(&ulp->strm, ulp->filters, LZMA_CHECK_CRC32); if (ret != LZMA_OK) { if (ret == LZMA_MEMLIMIT_ERROR) errx(1, "can't compress data: LZMA_MEMLIMIT_ERROR"); errx(1, "can't compress data: LZMA compressor ERROR"); } ulp->strm.next_in = iblk->data; ulp->strm.avail_in = iblk->info.len; ulp->strm.next_out = oblk->data; ulp->strm.avail_out = oblk->alen; ret = lzma_code(&ulp->strm, LZMA_FINISH); if (ret != LZMA_STREAM_END) errx(1, "lzma_code FINISH failed, code=%d, pos(in=%zd, " "out=%zd)", ret, (iblk->info.len - ulp->strm.avail_in), (oblk->alen - ulp->strm.avail_out)); #if 0 lzma_end(&ulp->strm); #endif oblk->info.len = oblk->alen - ulp->strm.avail_out; } diff --git a/usr.bin/mkuzip/mkuz_zlib.c b/usr.bin/mkuzip/mkuz_zlib.c index 7ec6fe378235..57f6079c3c54 100644 --- a/usr.bin/mkuzip/mkuz_zlib.c +++ b/usr.bin/mkuzip/mkuz_zlib.c @@ -1,82 +1,81 @@ /* * Copyright (c) 2004-2016 Maxim Sobolev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include "mkuzip.h" #include "mkuz_blk.h" #include "mkuz_zlib.h" struct mkuz_zlib { int comp_level; }; size_t mkuz_zlib_cbound(size_t blksz) { return (compressBound(blksz)); } void * mkuz_zlib_init(int *comp_level) { struct mkuz_zlib *zp; if (*comp_level == USE_DEFAULT_LEVEL) *comp_level = Z_BEST_COMPRESSION; if (*comp_level < Z_BEST_SPEED || *comp_level > Z_BEST_COMPRESSION) errx(1, "provided compression level %d is invalid", *comp_level); /* Not reached */ zp = mkuz_safe_zmalloc(sizeof(struct mkuz_zlib)); zp->comp_level = *comp_level; return (zp); } void mkuz_zlib_compress(void *p, const struct mkuz_blk *iblk, struct mkuz_blk *oblk) { uLongf destlen_z; struct mkuz_zlib *zp; zp = (struct mkuz_zlib *)p; destlen_z = oblk->alen; if (compress2(oblk->data, &destlen_z, iblk->data, iblk->info.len, zp->comp_level) != Z_OK) { errx(1, "can't compress data: compress2() failed"); /* Not reached */ } oblk->info.len = (uint32_t)destlen_z; } diff --git a/usr.bin/mkuzip/mkuzip.c b/usr.bin/mkuzip/mkuzip.c index 05d974a77748..f627562af7df 100644 --- a/usr.bin/mkuzip/mkuzip.c +++ b/usr.bin/mkuzip/mkuzip.c @@ -1,503 +1,502 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2004-2016 Maxim Sobolev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mkuzip.h" #include "mkuz_cloop.h" #include "mkuz_blockcache.h" #include "mkuz_lzma.h" #include "mkuz_zlib.h" #include "mkuz_zstd.h" #include "mkuz_blk.h" #include "mkuz_cfg.h" #include "mkuz_conveyor.h" #include "mkuz_format.h" #include "mkuz_fqueue.h" #include "mkuz_time.h" #include "mkuz_insize.h" #define DEFAULT_CLSTSIZE 16384 enum UZ_ALGORITHM { UZ_ZLIB = 0, UZ_LZMA, UZ_ZSTD, UZ_INVALID }; static const struct mkuz_format uzip_fmts[] = { [UZ_ZLIB] = { .option = "zlib", .magic = CLOOP_MAGIC_ZLIB, .default_sufx = DEFAULT_SUFX_ZLIB, .f_compress_bound = mkuz_zlib_cbound, .f_init = mkuz_zlib_init, .f_compress = mkuz_zlib_compress, }, [UZ_LZMA] = { .option = "lzma", .magic = CLOOP_MAGIC_LZMA, .default_sufx = DEFAULT_SUFX_LZMA, .f_compress_bound = mkuz_lzma_cbound, .f_init = mkuz_lzma_init, .f_compress = mkuz_lzma_compress, }, [UZ_ZSTD] = { .option = "zstd", .magic = CLOOP_MAGIC_ZSTD, .default_sufx = DEFAULT_SUFX_ZSTD, .f_compress_bound = mkuz_zstd_cbound, .f_init = mkuz_zstd_init, .f_compress = mkuz_zstd_compress, }, }; static struct mkuz_blk *readblock(int, u_int32_t); static void usage(void) __dead2; static void cleanup(void); static char *cleanfile = NULL; static int cmp_blkno(const struct mkuz_blk *bp, void *p) { uint32_t *ap; ap = (uint32_t *)p; return (bp->info.blkno == *ap); } int main(int argc, char **argv) { struct mkuz_cfg cfs; char *oname; uint64_t *toc; int i, io, opt, tmp; struct { int en; FILE *f; } summary; struct iovec iov[2]; uint64_t offset, last_offset; struct cloop_header hdr; struct mkuz_conveyor *cvp; struct mkuz_blk_info *chit; size_t ncpusz, ncpu, magiclen; double st, et; enum UZ_ALGORITHM comp_alg; int comp_level; st = getdtime(); ncpusz = sizeof(size_t); if (sysctlbyname("hw.ncpu", &ncpu, &ncpusz, NULL, 0) < 0) { ncpu = 1; } else if (ncpu > MAX_WORKERS_AUTO) { ncpu = MAX_WORKERS_AUTO; } memset(&hdr, 0, sizeof(hdr)); cfs.blksz = DEFAULT_CLSTSIZE; oname = NULL; cfs.verbose = 0; cfs.no_zcomp = 0; cfs.en_dedup = 0; summary.en = 0; summary.f = stderr; comp_alg = UZ_ZLIB; comp_level = USE_DEFAULT_LEVEL; cfs.nworkers = ncpu; struct mkuz_blk *iblk, *oblk; while((opt = getopt(argc, argv, "A:C:o:s:vZdLSj:")) != -1) { switch(opt) { case 'A': for (tmp = UZ_ZLIB; tmp < UZ_INVALID; tmp++) { if (strcmp(uzip_fmts[tmp].option, optarg) == 0) break; } if (tmp == UZ_INVALID) errx(1, "invalid algorithm specified: %s", optarg); /* Not reached */ comp_alg = tmp; break; case 'C': comp_level = atoi(optarg); break; case 'o': oname = optarg; break; case 's': tmp = atoi(optarg); if (tmp <= 0) { errx(1, "invalid cluster size specified: %s", optarg); /* Not reached */ } cfs.blksz = tmp; break; case 'v': cfs.verbose = 1; break; case 'Z': cfs.no_zcomp = 1; break; case 'd': cfs.en_dedup = 1; break; case 'L': comp_alg = UZ_LZMA; break; case 'S': summary.en = 1; summary.f = stdout; break; case 'j': tmp = atoi(optarg); if (tmp <= 0) { errx(1, "invalid number of compression threads" " specified: %s", optarg); /* Not reached */ } cfs.nworkers = tmp; break; default: usage(); /* Not reached */ } } argc -= optind; argv += optind; if (argc != 1) { usage(); /* Not reached */ } cfs.handler = &uzip_fmts[comp_alg]; magiclen = strlcpy(hdr.magic, cfs.handler->magic, sizeof(hdr.magic)); assert(magiclen < sizeof(hdr.magic)); if (cfs.en_dedup != 0) { /* * Dedupe requires a version 3 format. Don't downgrade newer * formats. */ if (hdr.magic[CLOOP_OFS_VERSN] == CLOOP_MAJVER_2) hdr.magic[CLOOP_OFS_VERSN] = CLOOP_MAJVER_3; hdr.magic[CLOOP_OFS_COMPR] = tolower(hdr.magic[CLOOP_OFS_COMPR]); } if (cfs.blksz % DEV_BSIZE != 0) errx(1, "cluster size should be multiple of %d", DEV_BSIZE); cfs.cbound_blksz = cfs.handler->f_compress_bound(cfs.blksz); if (cfs.cbound_blksz > MAXPHYS) errx(1, "maximal compressed cluster size %zu greater than MAXPHYS %zu", cfs.cbound_blksz, (size_t)MAXPHYS); cfs.handler->f_init(&comp_level); cfs.comp_level = comp_level; cfs.iname = argv[0]; if (oname == NULL) { asprintf(&oname, "%s%s", cfs.iname, cfs.handler->default_sufx); if (oname == NULL) { err(1, "can't allocate memory"); /* Not reached */ } } signal(SIGHUP, exit); signal(SIGINT, exit); signal(SIGTERM, exit); signal(SIGXCPU, exit); signal(SIGXFSZ, exit); atexit(cleanup); cfs.fdr = open(cfs.iname, O_RDONLY); if (cfs.fdr < 0) { err(1, "open(%s)", cfs.iname); /* Not reached */ } cfs.isize = mkuz_get_insize(&cfs); if (cfs.isize < 0) { errx(1, "can't determine input image size"); /* Not reached */ } hdr.nblocks = cfs.isize / cfs.blksz; if ((cfs.isize % cfs.blksz) != 0) { if (cfs.verbose != 0) fprintf(stderr, "file size is not multiple " "of %d, padding data\n", cfs.blksz); hdr.nblocks++; } toc = mkuz_safe_malloc((hdr.nblocks + 1) * sizeof(*toc)); /* * Initialize last+1 entry with non-heap trash. If final padding is * added later, it may or may not be overwritten with an offset * representing the length of the final compressed block. If not, * initialize to a defined value. */ toc[hdr.nblocks] = 0; cfs.fdw = open(oname, (cfs.en_dedup ? O_RDWR : O_WRONLY) | O_TRUNC | O_CREAT, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); if (cfs.fdw < 0) { err(1, "open(%s)", oname); /* Not reached */ } cleanfile = oname; /* Prepare header that we will write later when we have index ready. */ iov[0].iov_base = (char *)&hdr; iov[0].iov_len = sizeof(hdr); iov[1].iov_base = (char *)toc; iov[1].iov_len = (hdr.nblocks + 1) * sizeof(*toc); offset = iov[0].iov_len + iov[1].iov_len; /* Reserve space for header */ lseek(cfs.fdw, offset, SEEK_SET); if (cfs.verbose != 0) { fprintf(stderr, "data size %ju bytes, number of clusters " "%u, index length %zu bytes\n", cfs.isize, hdr.nblocks, iov[1].iov_len); } cvp = mkuz_conveyor_ctor(&cfs); last_offset = 0; iblk = oblk = NULL; for(i = io = 0; iblk != MKUZ_BLK_EOF; i++) { iblk = readblock(cfs.fdr, cfs.blksz); mkuz_fqueue_enq(cvp->wrk_queue, iblk); if (iblk != MKUZ_BLK_EOF && (i < (cfs.nworkers * ITEMS_PER_WORKER))) { continue; } drain: oblk = mkuz_fqueue_deq_when(cvp->results, cmp_blkno, &io); assert(oblk->info.blkno == (unsigned)io); oblk->info.offset = offset; chit = NULL; if (cfs.en_dedup != 0 && oblk->info.len > 0) { chit = mkuz_blkcache_regblock(cfs.fdw, oblk); /* * There should be at least one non-empty block * between us and the backref'ed offset, otherwise * we won't be able to parse that sequence correctly * as it would be indistinguishible from another * empty block. */ if (chit != NULL && chit->offset == last_offset) { chit = NULL; } } if (chit != NULL) { toc[io] = htobe64(chit->offset); oblk->info.len = 0; } else { if (oblk->info.len > 0 && write(cfs.fdw, oblk->data, oblk->info.len) < 0) { err(1, "write(%s)", oname); /* Not reached */ } toc[io] = htobe64(offset); last_offset = offset; offset += oblk->info.len; } if (cfs.verbose != 0) { fprintf(stderr, "cluster #%d, in %u bytes, " "out len=%lu offset=%lu", io, cfs.blksz, (u_long)oblk->info.len, (u_long)be64toh(toc[io])); if (chit != NULL) { fprintf(stderr, " (backref'ed to #%d)", chit->blkno); } fprintf(stderr, "\n"); } free(oblk); io += 1; if (iblk == MKUZ_BLK_EOF) { if (io < i) goto drain; /* Last block, see if we need to add some padding */ if ((offset % DEV_BSIZE) == 0) continue; oblk = mkuz_blk_ctor(DEV_BSIZE - (offset % DEV_BSIZE)); oblk->info.blkno = io; oblk->info.len = oblk->alen; if (cfs.verbose != 0) { fprintf(stderr, "padding data with %lu bytes " "so that file size is multiple of %d\n", (u_long)oblk->alen, DEV_BSIZE); } mkuz_fqueue_enq(cvp->results, oblk); goto drain; } } close(cfs.fdr); if (cfs.verbose != 0 || summary.en != 0) { et = getdtime(); fprintf(summary.f, "compressed data to %ju bytes, saved %lld " "bytes, %.2f%% decrease, %.2f bytes/sec.\n", offset, (long long)(cfs.isize - offset), 100.0 * (long long)(cfs.isize - offset) / (float)cfs.isize, (float)cfs.isize / (et - st)); } /* Convert to big endian */ hdr.blksz = htonl(cfs.blksz); hdr.nblocks = htonl(hdr.nblocks); /* Write headers into pre-allocated space */ lseek(cfs.fdw, 0, SEEK_SET); if (writev(cfs.fdw, iov, 2) < 0) { err(1, "writev(%s)", oname); /* Not reached */ } cleanfile = NULL; close(cfs.fdw); exit(0); } static struct mkuz_blk * readblock(int fd, u_int32_t clstsize) { int numread; struct mkuz_blk *rval; static int blockcnt; off_t cpos; rval = mkuz_blk_ctor(clstsize); rval->info.blkno = blockcnt; blockcnt += 1; cpos = lseek(fd, 0, SEEK_CUR); if (cpos < 0) { err(1, "readblock: lseek() failed"); /* Not reached */ } rval->info.offset = cpos; numread = read(fd, rval->data, clstsize); if (numread < 0) { err(1, "readblock: read() failed"); /* Not reached */ } if (numread == 0) { free(rval); return MKUZ_BLK_EOF; } rval->info.len = numread; return rval; } static void usage(void) { fprintf(stderr, "usage: mkuzip [-vZdLS] [-o outfile] [-s cluster_size] " "[-j ncompr] infile\n"); exit(1); } void * mkuz_safe_malloc(size_t size) { void *retval; retval = malloc(size); if (retval == NULL) { err(1, "can't allocate memory"); /* Not reached */ } return retval; } void * mkuz_safe_zmalloc(size_t size) { void *retval; retval = mkuz_safe_malloc(size); bzero(retval, size); return retval; } static void cleanup(void) { if (cleanfile != NULL) unlink(cleanfile); } int mkuz_memvcmp(const void *memory, unsigned char val, size_t size) { const u_char *mm; mm = (const u_char *)memory; return (*mm == val) && memcmp(mm, mm + 1, size - 1) == 0; } diff --git a/usr.bin/netstat/bpf.c b/usr.bin/netstat/bpf.c index c9b28b5eb085..c0cea882df76 100644 --- a/usr.bin/netstat/bpf.c +++ b/usr.bin/netstat/bpf.c @@ -1,169 +1,168 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2005 Christian S.J. Peron * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" /* print bpf stats */ static char * bpf_pidname(pid_t pid) { struct kinfo_proc newkp; int error, mib[4]; size_t size; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = pid; size = sizeof(newkp); error = sysctl(mib, 4, &newkp, &size, NULL, 0); if (error < 0) { xo_warn("kern.proc.pid failed"); return (strdup("??????")); } return (strdup(newkp.ki_comm)); } static void bpf_flags(struct xbpf_d *bd, char *flagbuf) { *flagbuf++ = bd->bd_promisc ? 'p' : '-'; *flagbuf++ = bd->bd_immediate ? 'i' : '-'; *flagbuf++ = bd->bd_hdrcmplt ? '-' : 'f'; *flagbuf++ = (bd->bd_direction == BPF_D_IN) ? '-' : ((bd->bd_direction == BPF_D_OUT) ? 'o' : 's'); *flagbuf++ = bd->bd_feedback ? 'b' : '-'; *flagbuf++ = bd->bd_async ? 'a' : '-'; *flagbuf++ = bd->bd_locked ? 'l' : '-'; *flagbuf++ = '\0'; if (bd->bd_promisc) xo_emit("{e:promiscuous/}"); if (bd->bd_immediate) xo_emit("{e:immediate/}"); if (bd->bd_hdrcmplt) xo_emit("{e:header-complete/}"); xo_emit("{e:direction}", (bd->bd_direction == BPF_D_IN) ? "input" : (bd->bd_direction == BPF_D_OUT) ? "output" : "bidirectional"); if (bd->bd_feedback) xo_emit("{e:feedback/}"); if (bd->bd_async) xo_emit("{e:async/}"); if (bd->bd_locked) xo_emit("{e:locked/}"); } void bpf_stats(char *ifname) { struct xbpf_d *d, *bd, zerostat; char *pname, flagbuf[12]; size_t size; if (zflag) { bzero(&zerostat, sizeof(zerostat)); if (sysctlbyname("net.bpf.stats", NULL, NULL, &zerostat, sizeof(zerostat)) < 0) xo_warn("failed to zero bpf counters"); return; } if (sysctlbyname("net.bpf.stats", NULL, &size, NULL, 0) < 0) { xo_warn("net.bpf.stats"); return; } if (size == 0) return; bd = malloc(size); if (bd == NULL) { xo_warn("malloc failed"); return; } if (sysctlbyname("net.bpf.stats", bd, &size, NULL, 0) < 0) { xo_warn("net.bpf.stats"); free(bd); return; } xo_emit("{T:/%5s} {T:/%6s} {T:/%7s} {T:/%9s} {T:/%9s} {T:/%9s} " "{T:/%5s} {T:/%5s} {T:/%s}\n", "Pid", "Netif", "Flags", "Recv", "Drop", "Match", "Sblen", "Hblen", "Command"); xo_open_container("bpf-statistics"); xo_open_list("bpf-entry"); for (d = &bd[0]; d < &bd[size / sizeof(*d)]; d++) { if (d->bd_structsize != sizeof(*d)) { xo_warnx("bpf_stats_extended: version mismatch"); return; } if (ifname && strcmp(ifname, d->bd_ifname) != 0) continue; xo_open_instance("bpf-entry"); pname = bpf_pidname(d->bd_pid); xo_emit("{k:pid/%5d} {k:interface-name/%6s} ", d->bd_pid, d->bd_ifname); bpf_flags(d, flagbuf); xo_emit("{d:flags/%7s} {:received-packets/%9ju} " "{:dropped-packets/%9ju} {:filter-packets/%9ju} " "{:store-buffer-length/%5d} {:hold-buffer-length/%5d} " "{:process/%s}\n", flagbuf, (uintmax_t)d->bd_rcount, (uintmax_t)d->bd_dcount, (uintmax_t)d->bd_fcount, d->bd_slen, d->bd_hlen, pname); free(pname); xo_close_instance("bpf-entry"); } xo_close_list("bpf-entry"); xo_close_container("bpf-statistics"); free(bd); } diff --git a/usr.bin/netstat/common.c b/usr.bin/netstat/common.c index 4b1a778737dc..64e26bf3c21e 100644 --- a/usr.bin/netstat/common.c +++ b/usr.bin/netstat/common.c @@ -1,138 +1,137 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" #include "common.h" const char * fmt_flags(const struct bits *p, int f) { static char name[33]; char *flags; for (flags = name; p->b_mask; p++) if (p->b_mask & f) *flags++ = p->b_val; *flags = '\0'; return (name); } void print_flags_generic(int flags, const struct bits *pbits, const char *format, const char *tag_name) { const struct bits *p; char tag_fmt[64]; xo_emit(format, fmt_flags(pbits, flags)); snprintf(tag_fmt, sizeof(tag_fmt), "{le:%s/%%s}", tag_name); xo_open_list(tag_name); for (p = pbits; p->b_mask; p++) if (p->b_mask & flags) xo_emit(tag_fmt, p->b_name); xo_close_list(tag_name); } struct ifmap_entry * prepare_ifmap(size_t *pifmap_size) { int ifindex = 0, size; struct ifaddrs *ifap, *ifa; struct sockaddr_dl *sdl; struct ifmap_entry *ifmap = NULL; int ifmap_size = 0; /* * Retrieve interface list at first * since we need #ifindex -> if_xname match */ if (getifaddrs(&ifap) != 0) err(EX_OSERR, "getifaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; sdl = (struct sockaddr_dl *)ifa->ifa_addr; ifindex = sdl->sdl_index; if (ifindex >= ifmap_size) { size = roundup2(ifindex + 1, 32) * sizeof(struct ifmap_entry); if ((ifmap = realloc(ifmap, size)) == NULL) errx(2, "realloc(%d) failed", size); memset(&ifmap[ifmap_size], 0, size - ifmap_size * sizeof(struct ifmap_entry)); ifmap_size = roundup2(ifindex + 1, 32); } if (*ifmap[ifindex].ifname != '\0') continue; strlcpy(ifmap[ifindex].ifname, ifa->ifa_name, IFNAMSIZ); } freeifaddrs(ifap); *pifmap_size = ifmap_size; return (ifmap); } diff --git a/usr.bin/netstat/if.c b/usr.bin/netstat/if.c index c6668cae10e8..7aafdf78b296 100644 --- a/usr.bin/netstat/if.c +++ b/usr.bin/netstat/if.c @@ -1,719 +1,715 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2013 Gleb Smirnoff * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PF #include #include #endif #include #include #include #include #ifdef INET6 #include #endif #include #include #include #include #include #include #include #include #include #include "netstat.h" static void sidewaysintpr(void); #ifdef PF static const char* pfsyncacts[] = { /* PFSYNC_ACT_CLR */ "clear all request", /* PFSYNC_ACT_INS_1301 */ "13.1 state insert", /* PFSYNC_ACT_INS_ACK */ "state inserted ack", /* PFSYNC_ACT_UPD_1301 */ "13.1 state update", /* PFSYNC_ACT_UPD_C */ "compressed state update", /* PFSYNC_ACT_UPD_REQ */ "uncompressed state request", /* PFSYNC_ACT_DEL */ "state delete", /* PFSYNC_ACT_DEL_C */ "compressed state delete", /* PFSYNC_ACT_INS_F */ "fragment insert", /* PFSYNC_ACT_DEL_F */ "fragment delete", /* PFSYNC_ACT_BUS */ "bulk update mark", /* PFSYNC_ACT_TDB */ "TDB replay counter update", /* PFSYNC_ACT_EOF */ "end of frame mark", /* PFSYNC_ACT_INS_1400 */ "state insert", /* PFSYNC_ACT_UPD_1400 */ "state update", }; static const char* pfsyncacts_name[] = { /* PFSYNC_ACT_CLR */ "clear-all-request", /* PFSYNC_ACT_INS_1301 */ "state-insert-1301", /* PFSYNC_ACT_INS_ACK */ "state-inserted-ack", /* PFSYNC_ACT_UPD_1301 */ "state-update-1301", /* PFSYNC_ACT_UPD_C */ "compressed-state-update", /* PFSYNC_ACT_UPD_REQ */ "uncompressed-state-request", /* PFSYNC_ACT_DEL */ "state-delete", /* PFSYNC_ACT_DEL_C */ "compressed-state-delete", /* PFSYNC_ACT_INS_F */ "fragment-insert", /* PFSYNC_ACT_DEL_F */ "fragment-delete", /* PFSYNC_ACT_BUS */ "bulk-update-mark", /* PFSYNC_ACT_TDB */ "TDB-replay-counter-update", /* PFSYNC_ACT_EOF */ "end-of-frame-mark", /* PFSYNC_ACT_INS_1400 */ "state-insert", /* PFSYNC_ACT_UPD_1400 */ "state-update", }; static void pfsync_acts_stats(const char *list, const char *desc, uint64_t *a) { int i; xo_open_list(list); for (i = 0; i < PFSYNC_ACT_MAX; i++, a++) { if (*a || sflag <= 1) { xo_open_instance(list); xo_emit("\t\t{e:name}{:count/%ju} {N:/%s%s %s}\n", pfsyncacts_name[i], (uintmax_t)(*a), pfsyncacts[i], plural(*a), desc); xo_close_instance(list); } } xo_close_list(list); } /* * Dump pfsync statistics structure. */ void pfsync_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct pfsyncstats pfsyncstat; if (fetch_stats("net.pfsync.stats", off, &pfsyncstat, sizeof(pfsyncstat), kread) != 0) return; xo_emit("{T:/%s}:\n", name); xo_open_container(name); #define p(f, m) if (pfsyncstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)pfsyncstat.f, plural(pfsyncstat.f)) p(pfsyncs_ipackets, "\t{:received-inet-packets/%ju} " "{N:/packet%s received (IPv4)}\n"); p(pfsyncs_ipackets6, "\t{:received-inet6-packets/%ju} " "{N:/packet%s received (IPv6)}\n"); pfsync_acts_stats("input-histogram", "received", &pfsyncstat.pfsyncs_iacts[0]); p(pfsyncs_badif, "\t\t{:dropped-bad-interface/%ju} " "{N:/packet%s discarded for bad interface}\n"); p(pfsyncs_badttl, "\t\t{:dropped-bad-ttl/%ju} " "{N:/packet%s discarded for bad ttl}\n"); p(pfsyncs_hdrops, "\t\t{:dropped-short-header/%ju} " "{N:/packet%s shorter than header}\n"); p(pfsyncs_badver, "\t\t{:dropped-bad-version/%ju} " "{N:/packet%s discarded for bad version}\n"); p(pfsyncs_badauth, "\t\t{:dropped-bad-auth/%ju} " "{N:/packet%s discarded for bad HMAC}\n"); p(pfsyncs_badact,"\t\t{:dropped-bad-action/%ju} " "{N:/packet%s discarded for bad action}\n"); p(pfsyncs_badlen, "\t\t{:dropped-short/%ju} " "{N:/packet%s discarded for short packet}\n"); p(pfsyncs_badval, "\t\t{:dropped-bad-values/%ju} " "{N:/state%s discarded for bad values}\n"); p(pfsyncs_stale, "\t\t{:dropped-stale-state/%ju} " "{N:/stale state%s}\n"); p(pfsyncs_badstate, "\t\t{:dropped-failed-lookup/%ju} " "{N:/failed state lookup\\/insert%s}\n"); p(pfsyncs_opackets, "\t{:sent-inet-packets/%ju} " "{N:/packet%s sent (IPv4})\n"); p(pfsyncs_opackets6, "\t{:send-inet6-packets/%ju} " "{N:/packet%s sent (IPv6})\n"); pfsync_acts_stats("output-histogram", "sent", &pfsyncstat.pfsyncs_oacts[0]); p(pfsyncs_onomem, "\t\t{:discarded-no-memory/%ju} " "{N:/failure%s due to mbuf memory error}\n"); p(pfsyncs_oerrors, "\t\t{:send-errors/%ju} " "{N:/send error%s}\n"); #undef p xo_close_container(name); } #endif /* PF */ /* * Display a formatted value, or a '-' in the same space. */ static void show_stat(const char *fmt, int width, const char *name, u_long value, short showvalue, int div1000) { const char *lsep, *rsep; char newfmt[64]; lsep = ""; if (strncmp(fmt, "LS", 2) == 0) { lsep = " "; fmt += 2; } rsep = " "; if (strncmp(fmt, "NRS", 3) == 0) { rsep = ""; fmt += 3; } if (showvalue == 0) { /* Print just dash. */ xo_emit("{P:/%s}{D:/%*s}{P:/%s}", lsep, width, "-", rsep); return; } /* * XXX: workaround {P:} modifier can't be empty and doesn't seem to * take args... so we need to conditionally include it in the format. */ #define maybe_pad(pad) do { \ if (strlen(pad)) { \ snprintf(newfmt, sizeof(newfmt), "{P:%s}", pad); \ xo_emit(newfmt); \ } \ } while (0) if (hflag) { char buf[5]; /* Format in human readable form. */ humanize_number(buf, sizeof(buf), (int64_t)value, "", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL | \ ((div1000) ? HN_DIVISOR_1000 : 0)); maybe_pad(lsep); snprintf(newfmt, sizeof(newfmt), "{:%s/%%%ds}", name, width); xo_emit(newfmt, buf); maybe_pad(rsep); } else { /* Construct the format string. */ maybe_pad(lsep); snprintf(newfmt, sizeof(newfmt), "{:%s/%%%d%s}", name, width, fmt); xo_emit(newfmt, value); maybe_pad(rsep); } } /* * Find next multiaddr for a given interface name. */ static struct ifmaddrs * next_ifma(struct ifmaddrs *ifma, const char *name, const sa_family_t family) { for(; ifma != NULL; ifma = ifma->ifma_next) { struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)ifma->ifma_name; if (ifma->ifma_addr->sa_family == family && strcmp(sdl->sdl_data, name) == 0) break; } return (ifma); } enum process_op { MEASURE, EMIT }; static void process_ifa_addr(enum process_op op, struct ifaddrs *ifa, int *max_net_len, int *max_addr_len, bool *network, bool *link) { int net_len, addr_len; const char *nn, *rn; if (op == EMIT) { net_len = *max_net_len; addr_len = *max_addr_len; } switch (ifa->ifa_addr->sa_family) { case AF_UNSPEC: if (op == MEASURE) { net_len = strlen("none"); addr_len = strlen("none"); } else { xo_emit("{:network/%-*.*s} ", net_len, net_len, "none"); xo_emit("{:address/%-*.*s} ", addr_len, addr_len, "none"); } break; case AF_INET: #ifdef INET6 case AF_INET6: #endif /* INET6 */ nn = netname(ifa->ifa_addr, ifa->ifa_netmask); rn = routename(ifa->ifa_addr, numeric_addr); if (op == MEASURE) { net_len = strlen(nn); addr_len = strlen(rn); } else { xo_emit("{t:network/%-*s} ", net_len, nn); xo_emit("{t:address/%-*s} ", addr_len, rn); } if (network != NULL) *network = true; break; case AF_LINK: { struct sockaddr_dl *sdl; char linknum[sizeof("")]; sdl = (struct sockaddr_dl *)ifa->ifa_addr; snprintf(linknum, sizeof(linknum), "", sdl->sdl_index); if (op == MEASURE) { net_len = strlen(linknum); if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) addr_len = 1; else addr_len = strlen(routename(ifa->ifa_addr, 1)); } else { xo_emit("{t:network/%-*.*s} ", net_len, net_len, linknum); if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) xo_emit("{P:/%*s} ", addr_len, ""); else xo_emit("{t:address/%-*.*s} ", addr_len, addr_len, routename(ifa->ifa_addr, 1)); } if (link != NULL) *link = true; break; } } if (op == MEASURE) { if (net_len > *max_net_len) *max_net_len = net_len; if (addr_len > *max_addr_len) *max_addr_len = addr_len; } } static int max_num_len(int max_len, u_long num) { int len = 2; /* include space */ for (; num > 10; len++) num /= 10; return (MAX(max_len, len)); } /* * Print a description of the network interfaces. */ void intpr(void (*pfunc)(char *), int af) { struct ifaddrs *ifap, *ifa; struct ifmaddrs *ifmap, *ifma; u_int ifn_len_max = 5, ifn_len; u_int net_len = strlen("Network "), addr_len = strlen("Address "); u_int npkt_len = 8, nbyte_len = 10, nerr_len = 5; if (interval) return sidewaysintpr(); if (getifaddrs(&ifap) != 0) err(EX_OSERR, "getifaddrs"); if (aflag && getifmaddrs(&ifmap) != 0) err(EX_OSERR, "getifmaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (interface != NULL && strcmp(ifa->ifa_name, interface) != 0) continue; if (af != AF_UNSPEC && ifa->ifa_addr->sa_family != af) continue; ifn_len = strlen(ifa->ifa_name); if ((ifa->ifa_flags & IFF_UP) == 0) ++ifn_len; ifn_len_max = MAX(ifn_len_max, ifn_len); process_ifa_addr(MEASURE, ifa, &net_len, &addr_len, NULL, NULL); #define IFA_STAT(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s) if (!hflag) { npkt_len = max_num_len(npkt_len, IFA_STAT(ipackets)); npkt_len = max_num_len(npkt_len, IFA_STAT(opackets)); nerr_len = max_num_len(nerr_len, IFA_STAT(ierrors)); nerr_len = max_num_len(nerr_len, IFA_STAT(iqdrops)); nerr_len = max_num_len(nerr_len, IFA_STAT(collisions)); if (dflag) nerr_len = max_num_len(nerr_len, IFA_STAT(oqdrops)); if (bflag) { nbyte_len = max_num_len(nbyte_len, IFA_STAT(ibytes)); nbyte_len = max_num_len(nbyte_len, IFA_STAT(obytes)); } } } xo_open_list("interface"); if (!pfunc) { xo_emit("{T:/%-*.*s}", ifn_len_max, ifn_len_max, "Name"); xo_emit(" {T:/%5.5s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} " "{T:/%*.*s} {T:/%*.*s}", "Mtu", net_len, net_len, "Network", addr_len, addr_len, "Address", npkt_len, npkt_len, "Ipkts", nerr_len, nerr_len, "Ierrs", nerr_len, nerr_len, "Idrop"); if (bflag) xo_emit(" {T:/%*.*s}", nbyte_len, nbyte_len, "Ibytes"); xo_emit(" {T:/%*.*s} {T:/%*.*s}", npkt_len, npkt_len, "Opkts", nerr_len, nerr_len, "Oerrs"); if (bflag) xo_emit(" {T:/%*.*s}", nbyte_len, nbyte_len, "Obytes"); xo_emit(" {T:/%*s}", nerr_len, "Coll"); if (dflag) xo_emit(" {T:/%*.*s}", nerr_len, nerr_len, "Drop"); xo_emit("\n"); } for (ifa = ifap; ifa; ifa = ifa->ifa_next) { bool network = false, link = false; char *name, *xname, buf[IFNAMSIZ+1]; if (interface != NULL && strcmp(ifa->ifa_name, interface) != 0) continue; name = ifa->ifa_name; if (pfunc) { (*pfunc)(name); /* * Skip all ifaddrs belonging to same interface. */ while(ifa->ifa_next != NULL && (strcmp(ifa->ifa_next->ifa_name, name) == 0)) { ifa = ifa->ifa_next; } continue; } if (af != AF_UNSPEC && ifa->ifa_addr->sa_family != af) continue; xo_open_instance("interface"); if ((ifa->ifa_flags & IFF_UP) == 0) { xname = stpcpy(buf, name); *xname++ = '*'; *xname = '\0'; xname = buf; } else xname = name; xo_emit("{d:/%-*.*s}{etk:name}{eq:flags/0x%x}", ifn_len_max, ifn_len_max, xname, name, ifa->ifa_flags); #define IFA_MTU(ifa) (((struct if_data *)(ifa)->ifa_data)->ifi_mtu) show_stat("lu", 6, "mtu", IFA_MTU(ifa), IFA_MTU(ifa), 0); #undef IFA_MTU process_ifa_addr(EMIT, ifa, &net_len, &addr_len, &network, &link); show_stat("lu", npkt_len, "received-packets", IFA_STAT(ipackets), link|network, 1); show_stat("lu", nerr_len, "received-errors", IFA_STAT(ierrors), link, 1); show_stat("lu", nerr_len, "dropped-packets", IFA_STAT(iqdrops), link, 1); if (bflag) show_stat("lu", nbyte_len, "received-bytes", IFA_STAT(ibytes), link|network, 0); show_stat("lu", npkt_len, "sent-packets", IFA_STAT(opackets), link|network, 1); show_stat("lu", nerr_len, "send-errors", IFA_STAT(oerrors), link, 1); if (bflag) show_stat("lu", nbyte_len, "sent-bytes", IFA_STAT(obytes), link|network, 0); show_stat("NRSlu", nerr_len, "collisions", IFA_STAT(collisions), link, 1); if (dflag) show_stat("LSlu", nerr_len, "dropped-packets", IFA_STAT(oqdrops), link, 1); xo_emit("\n"); if (!aflag) { xo_close_instance("interface"); continue; } /* * Print family's multicast addresses. */ xo_open_list("multicast-address"); for (ifma = next_ifma(ifmap, ifa->ifa_name, ifa->ifa_addr->sa_family); ifma != NULL; ifma = next_ifma(ifma, ifa->ifa_name, ifa->ifa_addr->sa_family)) { const char *fmt = NULL; xo_open_instance("multicast-address"); switch (ifma->ifma_addr->sa_family) { case AF_LINK: { struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)ifma->ifma_addr; if (sdl->sdl_type != IFT_ETHER && sdl->sdl_type != IFT_FDDI) break; } /* FALLTHROUGH */ case AF_INET: #ifdef INET6 case AF_INET6: #endif /* INET6 */ fmt = routename(ifma->ifma_addr, numeric_addr); break; } if (fmt) { if (Wflag) xo_emit("{P:/%27s }" "{t:address/%-17s/}", "", fmt); else xo_emit("{P:/%25s }" "{t:address/%-17.17s/}", "", fmt); if (ifma->ifma_addr->sa_family == AF_LINK) { xo_emit(" {:received-packets/%8lu}", IFA_STAT(imcasts)); xo_emit("{P:/%*s}", bflag? 17 : 6, ""); xo_emit(" {:sent-packets/%8lu}", IFA_STAT(omcasts)); } xo_emit("\n"); } xo_close_instance("multicast-address"); ifma = ifma->ifma_next; } xo_close_list("multicast-address"); xo_close_instance("interface"); } xo_close_list("interface"); freeifaddrs(ifap); if (aflag) freeifmaddrs(ifmap); } struct iftot { u_long ift_ip; /* input packets */ u_long ift_ie; /* input errors */ u_long ift_id; /* input drops */ u_long ift_op; /* output packets */ u_long ift_oe; /* output errors */ u_long ift_od; /* output drops */ u_long ift_co; /* collisions */ u_long ift_ib; /* input bytes */ u_long ift_ob; /* output bytes */ }; /* * Obtain stats for interface(s). */ static void fill_iftot(struct iftot *st) { struct ifaddrs *ifap, *ifa; bool found = false; if (getifaddrs(&ifap) != 0) xo_err(EX_OSERR, "getifaddrs"); bzero(st, sizeof(*st)); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; if (interface) { if (strcmp(ifa->ifa_name, interface) == 0) found = true; else continue; } st->ift_ip += IFA_STAT(ipackets); st->ift_ie += IFA_STAT(ierrors); st->ift_id += IFA_STAT(iqdrops); st->ift_ib += IFA_STAT(ibytes); st->ift_op += IFA_STAT(opackets); st->ift_oe += IFA_STAT(oerrors); st->ift_od += IFA_STAT(oqdrops); st->ift_ob += IFA_STAT(obytes); st->ift_co += IFA_STAT(collisions); } if (interface && found == false) xo_err(EX_DATAERR, "interface %s not found", interface); freeifaddrs(ifap); } /* * Set a flag to indicate that a signal from the periodic itimer has been * caught. */ static sig_atomic_t signalled; static void catchalarm(int signo __unused) { signalled = true; } /* * Print a running summary of interface statistics. * Repeat display every interval seconds, showing statistics * collected over that interval. Assumes that interval is non-zero. * First line printed at top of screen is always cumulative. */ static void sidewaysintpr(void) { struct iftot ift[2], *new, *old; struct itimerval interval_it; int oldmask, line; new = &ift[0]; old = &ift[1]; fill_iftot(old); (void)signal(SIGALRM, catchalarm); signalled = false; interval_it.it_interval.tv_sec = interval; interval_it.it_interval.tv_usec = 0; interval_it.it_value = interval_it.it_interval; setitimer(ITIMER_REAL, &interval_it, NULL); xo_open_list("interface-statistics"); banner: xo_emit("{T:/%17s} {T:/%14s} {T:/%16s}\n", "input", interface != NULL ? interface : "(Total)", "output"); xo_emit("{T:/%10s} {T:/%5s} {T:/%5s} {T:/%10s} {T:/%10s} {T:/%5s} " "{T:/%10s} {T:/%5s}", "packets", "errs", "idrops", "bytes", "packets", "errs", "bytes", "colls"); if (dflag) xo_emit(" {T:/%5.5s}", "drops"); xo_emit("\n"); xo_flush(); line = 0; loop: if ((noutputs != 0) && (--noutputs == 0)) { xo_close_list("interface-statistics"); return; } oldmask = sigblock(sigmask(SIGALRM)); while (!signalled) sigpause(0); signalled = false; sigsetmask(oldmask); line++; fill_iftot(new); xo_open_instance("stats"); show_stat("lu", 10, "received-packets", new->ift_ip - old->ift_ip, 1, 1); show_stat("lu", 5, "received-errors", new->ift_ie - old->ift_ie, 1, 1); show_stat("lu", 5, "dropped-packets", new->ift_id - old->ift_id, 1, 1); show_stat("lu", 10, "received-bytes", new->ift_ib - old->ift_ib, 1, 0); show_stat("lu", 10, "sent-packets", new->ift_op - old->ift_op, 1, 1); show_stat("lu", 5, "send-errors", new->ift_oe - old->ift_oe, 1, 1); show_stat("lu", 10, "sent-bytes", new->ift_ob - old->ift_ob, 1, 0); show_stat("NRSlu", 5, "collisions", new->ift_co - old->ift_co, 1, 1); if (dflag) show_stat("LSlu", 5, "dropped-packets", new->ift_od - old->ift_od, 1, 1); xo_close_instance("stats"); xo_emit("\n"); xo_flush(); if (new == &ift[0]) { new = &ift[1]; old = &ift[0]; } else { new = &ift[0]; old = &ift[1]; } if (line == 21) goto banner; else goto loop; /* NOTREACHED */ } diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c index 987b1ebd05c6..0657926eab80 100644 --- a/usr.bin/netstat/inet.c +++ b/usr.bin/netstat/inet.c @@ -1,1537 +1,1533 @@ /*- * Copyright (c) 1983, 1988, 1993, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. 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 -#endif - -#include #include #include #include #include #include #define _WANT_SOCKET #include #include #include #include #include #include #include #include #ifdef INET6 #include #endif /* INET6 */ #include #include #include #include #include #include #include #include #include #include #define TCPSTATES #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" #include "nl_defs.h" #define max(a, b) (((a) > (b)) ? (a) : (b)) #ifdef INET static void inetprint(const char *, struct in_addr *, int, const char *, int, const int); #endif #ifdef INET6 static int udp_done, tcp_done, sdp_done; #endif /* INET6 */ static int pcblist_sysctl(int proto, const char *name, char **bufp) { const char *mibvar; char *buf; size_t len; switch (proto) { case IPPROTO_TCP: mibvar = "net.inet.tcp.pcblist"; break; case IPPROTO_UDP: mibvar = "net.inet.udp.pcblist"; break; default: mibvar = "net.inet.raw.pcblist"; break; } if (strncmp(name, "sdp", 3) == 0) mibvar = "net.inet.sdp.pcblist"; else if (strncmp(name, "divert", 6) == 0) mibvar = "net.inet.divert.pcblist"; len = 0; if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { if (errno != ENOENT) xo_warn("sysctl: %s", mibvar); return (0); } if ((buf = malloc(len)) == NULL) { xo_warnx("malloc %lu bytes", (u_long)len); return (0); } if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { xo_warn("sysctl: %s", mibvar); free(buf); return (0); } *bufp = buf; return (1); } /* * Copied directly from uipc_socket2.c. We leave out some fields that are in * nested structures that aren't used to avoid extra work. */ static void sbtoxsockbuf(struct sockbuf *sb, struct xsockbuf *xsb) { xsb->sb_cc = sb->sb_ccc; xsb->sb_hiwat = sb->sb_hiwat; xsb->sb_mbcnt = sb->sb_mbcnt; xsb->sb_mbmax = sb->sb_mbmax; xsb->sb_lowat = sb->sb_lowat; xsb->sb_flags = sb->sb_flags; xsb->sb_timeo = sb->sb_timeo; } int sotoxsocket(struct socket *so, struct xsocket *xso) { struct protosw proto; struct domain domain; bzero(xso, sizeof *xso); xso->xso_len = sizeof *xso; xso->xso_so = (uintptr_t)so; xso->so_type = so->so_type; xso->so_options = so->so_options; xso->so_linger = so->so_linger; xso->so_state = so->so_state; xso->so_pcb = (uintptr_t)so->so_pcb; if (kread((uintptr_t)so->so_proto, &proto, sizeof(proto)) != 0) return (-1); xso->xso_protocol = proto.pr_protocol; if (kread((uintptr_t)proto.pr_domain, &domain, sizeof(domain)) != 0) return (-1); xso->xso_family = domain.dom_family; xso->so_timeo = so->so_timeo; xso->so_error = so->so_error; if ((so->so_options & SO_ACCEPTCONN) != 0) { xso->so_qlen = so->sol_qlen; xso->so_incqlen = so->sol_incqlen; xso->so_qlimit = so->sol_qlimit; } else { sbtoxsockbuf(&so->so_snd, &xso->so_snd); sbtoxsockbuf(&so->so_rcv, &xso->so_rcv); xso->so_oobmark = so->so_oobmark; } return (0); } /* * Print a summary of connections related to an Internet * protocol. For TCP, also give state of connection. * Listening processes (aflag) are suppressed unless the * -a (all) flag is specified. */ void protopr(u_long off, const char *name, int af1, int proto) { static int first = 1; int istcp; char *buf; const char *vchar; struct xtcpcb *tp; struct xinpcb *inp; struct xinpgen *xig, *oxig; struct xsocket *so; int fnamelen, cnamelen; istcp = 0; switch (proto) { case IPPROTO_TCP: #ifdef INET6 if (strncmp(name, "sdp", 3) != 0) { if (tcp_done != 0) return; else tcp_done = 1; } else { if (sdp_done != 0) return; else sdp_done = 1; } #endif istcp = 1; break; case IPPROTO_UDP: #ifdef INET6 if (udp_done != 0) return; else udp_done = 1; #endif break; } if (!pcblist_sysctl(proto, name, &buf)) return; if (istcp && (cflag || Cflag)) { fnamelen = strlen("Stack"); cnamelen = strlen("CC"); oxig = xig = (struct xinpgen *)buf; for (xig = (struct xinpgen*)((char *)xig + xig->xig_len); xig->xig_len > sizeof(struct xinpgen); xig = (struct xinpgen *)((char *)xig + xig->xig_len)) { tp = (struct xtcpcb *)xig; inp = &tp->xt_inp; if (inp->inp_gencnt > oxig->xig_gen) continue; so = &inp->xi_socket; if (so->xso_protocol != proto) continue; fnamelen = max(fnamelen, (int)strlen(tp->xt_stack)); cnamelen = max(cnamelen, (int)strlen(tp->xt_cc)); } } oxig = xig = (struct xinpgen *)buf; for (xig = (struct xinpgen *)((char *)xig + xig->xig_len); xig->xig_len > sizeof(struct xinpgen); xig = (struct xinpgen *)((char *)xig + xig->xig_len)) { if (istcp) { tp = (struct xtcpcb *)xig; inp = &tp->xt_inp; } else { inp = (struct xinpcb *)xig; } so = &inp->xi_socket; /* Ignore sockets for protocols other than the desired one. */ if (proto != 0 && so->xso_protocol != proto) continue; /* Ignore PCBs which were freed during copyout. */ if (inp->inp_gencnt > oxig->xig_gen) continue; if ((af1 == AF_INET && (inp->inp_vflag & INP_IPV4) == 0) #ifdef INET6 || (af1 == AF_INET6 && (inp->inp_vflag & INP_IPV6) == 0) #endif /* INET6 */ || (af1 == AF_UNSPEC && ((inp->inp_vflag & INP_IPV4) == 0 #ifdef INET6 && (inp->inp_vflag & INP_IPV6) == 0 #endif /* INET6 */ )) ) continue; if (!aflag && ( (istcp && tp->t_state == TCPS_LISTEN) || (af1 == AF_INET && inp->inp_laddr.s_addr == INADDR_ANY) #ifdef INET6 || (af1 == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) #endif /* INET6 */ || (af1 == AF_UNSPEC && (((inp->inp_vflag & INP_IPV4) != 0 && inp->inp_laddr.s_addr == INADDR_ANY) #ifdef INET6 || ((inp->inp_vflag & INP_IPV6) != 0 && IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) #endif )) )) continue; if (first) { if (!Lflag) { xo_emit("Active Internet connections"); if (aflag) xo_emit(" (including servers)"); } else xo_emit( "Current listen queue sizes (qlen/incqlen/maxqlen)"); xo_emit("\n"); if (Aflag) xo_emit("{T:/%-*s} ", 2 * (int)sizeof(void *), "Tcpcb"); if (Lflag) xo_emit((Aflag && !Wflag) ? "{T:/%-5.5s} {T:/%-32.32s} {T:/%-18.18s}" : ((!Wflag || af1 == AF_INET) ? "{T:/%-5.5s} {T:/%-32.32s} {T:/%-22.22s}" : "{T:/%-5.5s} {T:/%-32.32s} {T:/%-45.45s}"), "Proto", "Listen", "Local Address"); else if (Tflag) xo_emit((Aflag && !Wflag) ? "{T:/%-5.5s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-18.18s} {T:/%s}" : ((!Wflag || af1 == AF_INET) ? "{T:/%-5.5s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-22.22s} {T:/%s}" : "{T:/%-5.5s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-45.45s} {T:/%s}"), "Proto", "Rexmit", "OOORcv", "0-win", "Local Address", "Foreign Address"); else { xo_emit((Aflag && !Wflag) ? "{T:/%-5.5s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-18.18s} {T:/%-18.18s}" : ((!Wflag || af1 == AF_INET) ? "{T:/%-5.5s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-22.22s} {T:/%-22.22s}" : "{T:/%-5.5s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-45.45s} {T:/%-45.45s}"), "Proto", "Recv-Q", "Send-Q", "Local Address", "Foreign Address"); if (!xflag && !Rflag) xo_emit(" {T:/%-11.11s}", "(state)"); } if (xflag) { xo_emit("{T:/%-6.6s} {T:/%-6.6s} " "{T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} " "{T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s}", "R-HIWA", "S-HIWA", "R-LOWA", "S-LOWA", "R-BCNT", "S-BCNT", "R-BMAX", "S-BMAX"); xo_emit(" {T:/%7.7s} {T:/%7.7s} {T:/%7.7s} " "{T:/%7.7s} {T:/%7.7s} {T:/%7.7s}", "rexmt", "persist", "keep", "2msl", "delack", "rcvtime"); } else if (Rflag) { xo_emit(" {T:/%8.8s} {T:/%5.5s}", "flowid", "ftype"); } if (cflag) { xo_emit(" {T:/%-*.*s}", fnamelen, fnamelen, "Stack"); } if (Cflag) xo_emit(" {T:/%-*.*s} {T:/%10.10s}" " {T:/%10.10s} {T:/%5.5s}" " {T:/%3.3s}", cnamelen, cnamelen, "CC", "cwin", "ssthresh", "MSS", "ECN"); if (Pflag) xo_emit(" {T:/%s}", "Log ID"); xo_emit("\n"); first = 0; } if (Lflag && so->so_qlimit == 0) continue; xo_open_instance("socket"); if (Aflag) xo_emit("{q:address/%*lx} ", 2 * (int)sizeof(void *), (u_long)so->so_pcb); #ifdef INET6 if ((inp->inp_vflag & INP_IPV6) != 0) vchar = ((inp->inp_vflag & INP_IPV4) != 0) ? "46" : "6"; else #endif vchar = ((inp->inp_vflag & INP_IPV4) != 0) ? "4" : ""; if (istcp && (tp->t_flags & TF_TOE) != 0) xo_emit("{:protocol/%-3.3s%-2.2s/%s%s} ", "toe", vchar); else xo_emit("{:protocol/%-3.3s%-2.2s/%s%s} ", name, vchar); if (Lflag) { char buf1[33]; snprintf(buf1, sizeof buf1, "%u/%u/%u", so->so_qlen, so->so_incqlen, so->so_qlimit); xo_emit("{:listen-queue-sizes/%-32.32s} ", buf1); } else if (Tflag) { if (istcp) xo_emit("{:sent-retransmit-packets/%6u} " "{:received-out-of-order-packets/%6u} " "{:sent-zero-window/%6u} ", tp->t_sndrexmitpack, tp->t_rcvoopack, tp->t_sndzerowin); else xo_emit("{P:/%21s}", ""); } else { xo_emit("{:receive-bytes-waiting/%6u} " "{:send-bytes-waiting/%6u} ", so->so_rcv.sb_cc, so->so_snd.sb_cc); } if (numeric_port) { #ifdef INET if (inp->inp_vflag & INP_IPV4) { inetprint("local", &inp->inp_laddr, (int)inp->inp_lport, name, 1, af1); if (!Lflag) inetprint("remote", &inp->inp_faddr, (int)inp->inp_fport, name, 1, af1); } #endif #if defined(INET) && defined(INET6) else #endif #ifdef INET6 if (inp->inp_vflag & INP_IPV6) { inet6print("local", &inp->in6p_laddr, (int)inp->inp_lport, name, 1); if (!Lflag) inet6print("remote", &inp->in6p_faddr, (int)inp->inp_fport, name, 1); } /* else nothing printed now */ #endif /* INET6 */ } else if (inp->inp_flags & INP_ANONPORT) { #ifdef INET if (inp->inp_vflag & INP_IPV4) { inetprint("local", &inp->inp_laddr, (int)inp->inp_lport, name, 1, af1); if (!Lflag) inetprint("remote", &inp->inp_faddr, (int)inp->inp_fport, name, 0, af1); } #endif #if defined(INET) && defined(INET6) else #endif #ifdef INET6 if (inp->inp_vflag & INP_IPV6) { inet6print("local", &inp->in6p_laddr, (int)inp->inp_lport, name, 1); if (!Lflag) inet6print("remote", &inp->in6p_faddr, (int)inp->inp_fport, name, 0); } /* else nothing printed now */ #endif /* INET6 */ } else { #ifdef INET if (inp->inp_vflag & INP_IPV4) { inetprint("local", &inp->inp_laddr, (int)inp->inp_lport, name, 0, af1); if (!Lflag) inetprint("remote", &inp->inp_faddr, (int)inp->inp_fport, name, inp->inp_lport != inp->inp_fport, af1); } #endif #if defined(INET) && defined(INET6) else #endif #ifdef INET6 if (inp->inp_vflag & INP_IPV6) { inet6print("local", &inp->in6p_laddr, (int)inp->inp_lport, name, 0); if (!Lflag) inet6print("remote", &inp->in6p_faddr, (int)inp->inp_fport, name, inp->inp_lport != inp->inp_fport); } /* else nothing printed now */ #endif /* INET6 */ } if (xflag) { xo_emit("{:receive-high-water/%6u} " "{:send-high-water/%6u} " "{:receive-low-water/%6u} {:send-low-water/%6u} " "{:receive-mbuf-bytes/%6u} {:send-mbuf-bytes/%6u} " "{:receive-mbuf-bytes-max/%6u} " "{:send-mbuf-bytes-max/%6u}", so->so_rcv.sb_hiwat, so->so_snd.sb_hiwat, so->so_rcv.sb_lowat, so->so_snd.sb_lowat, so->so_rcv.sb_mbcnt, so->so_snd.sb_mbcnt, so->so_rcv.sb_mbmax, so->so_snd.sb_mbmax); if (istcp) xo_emit(" {:retransmit-timer/%4d.%02d} " "{:persist-timer/%4d.%02d} " "{:keepalive-timer/%4d.%02d} " "{:msl2-timer/%4d.%02d} " "{:delay-ack-timer/%4d.%02d} " "{:inactivity-timer/%4d.%02d}", tp->tt_rexmt / 1000, (tp->tt_rexmt % 1000) / 10, tp->tt_persist / 1000, (tp->tt_persist % 1000) / 10, tp->tt_keep / 1000, (tp->tt_keep % 1000) / 10, tp->tt_2msl / 1000, (tp->tt_2msl % 1000) / 10, tp->tt_delack / 1000, (tp->tt_delack % 1000) / 10, tp->t_rcvtime / 1000, (tp->t_rcvtime % 1000) / 10); } if (istcp && !Lflag && !xflag && !Tflag && !Rflag) { if (tp->t_state < 0 || tp->t_state >= TCP_NSTATES) xo_emit("{:tcp-state/%-11d}", tp->t_state); else { xo_emit("{:tcp-state/%-11s}", tcpstates[tp->t_state]); #if defined(TF_NEEDSYN) && defined(TF_NEEDFIN) /* Show T/TCP `hidden state' */ if (tp->t_flags & (TF_NEEDSYN|TF_NEEDFIN)) xo_emit("{:need-syn-or-fin/*}"); #endif /* defined(TF_NEEDSYN) && defined(TF_NEEDFIN) */ } } if (Rflag) { /* XXX: is this right Alfred */ xo_emit(" {:flow-id/%08x} {:flow-type/%5d}", inp->inp_flowid, inp->inp_flowtype); } if (istcp) { if (cflag) xo_emit(" {:stack/%-*.*s}", fnamelen, fnamelen, tp->xt_stack); if (Cflag) xo_emit(" {:cc/%-*.*s}" " {:snd-cwnd/%10lu}" " {:snd-ssthresh/%10lu}" " {:t-maxseg/%5u} {:ecn/%3s}", cnamelen, cnamelen, tp->xt_cc, tp->t_snd_cwnd, tp->t_snd_ssthresh, tp->t_maxseg, (tp->t_state >= TCPS_ESTABLISHED ? (tp->xt_ecn > 0 ? (tp->xt_ecn == 1 ? "ecn" : "ace") : "off") : "n/a")); if (Pflag) xo_emit(" {:log-id/%s}", tp->xt_logid[0] == '\0' ? "-" : tp->xt_logid); } xo_emit("\n"); xo_close_instance("socket"); } if (xig != oxig && xig->xig_gen != oxig->xig_gen) { if (oxig->xig_count > xig->xig_count) { xo_emit("Some {d:lost/%s} sockets may have been " "deleted.\n", name); } else if (oxig->xig_count < xig->xig_count) { xo_emit("Some {d:created/%s} sockets may have been " "created.\n", name); } else { xo_emit("Some {d:changed/%s} sockets may have been " "created or deleted.\n", name); } } free(buf); } /* * Dump TCP statistics structure. */ void tcp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct tcpstat tcpstat; uint64_t tcps_states[TCP_NSTATES]; #ifdef INET6 if (tcp_done != 0) return; else tcp_done = 1; #endif if (fetch_stats("net.inet.tcp.stats", off, &tcpstat, sizeof(tcpstat), kread_counters) != 0) return; if (fetch_stats_ro("net.inet.tcp.states", nl[N_TCPS_STATES].n_value, &tcps_states, sizeof(tcps_states), kread_counters) != 0) return; xo_open_container("tcp"); xo_emit("{T:/%s}:\n", name); #define p(f, m) if (tcpstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t )tcpstat.f, plural(tcpstat.f)) #define p1a(f, m) if (tcpstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t )tcpstat.f) #define p2(f1, f2, m) if (tcpstat.f1 || tcpstat.f2 || sflag <= 1) \ xo_emit(m, (uintmax_t )tcpstat.f1, plural(tcpstat.f1), \ (uintmax_t )tcpstat.f2, plural(tcpstat.f2)) #define p2a(f1, f2, m) if (tcpstat.f1 || tcpstat.f2 || sflag <= 1) \ xo_emit(m, (uintmax_t )tcpstat.f1, plural(tcpstat.f1), \ (uintmax_t )tcpstat.f2) #define p3(f, m) if (tcpstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t )tcpstat.f, pluralies(tcpstat.f)) p(tcps_sndtotal, "\t{:sent-packets/%ju} {N:/packet%s sent}\n"); p2(tcps_sndpack,tcps_sndbyte, "\t\t{:sent-data-packets/%ju} " "{N:/data packet%s} ({:sent-data-bytes/%ju} {N:/byte%s})\n"); p2(tcps_sndrexmitpack, tcps_sndrexmitbyte, "\t\t" "{:sent-retransmitted-packets/%ju} {N:/data packet%s} " "({:sent-retransmitted-bytes/%ju} {N:/byte%s}) " "{N:retransmitted}\n"); p(tcps_sndrexmitbad, "\t\t" "{:sent-unnecessary-retransmitted-packets/%ju} " "{N:/data packet%s unnecessarily retransmitted}\n"); p(tcps_mturesent, "\t\t{:sent-resends-by-mtu-discovery/%ju} " "{N:/resend%s initiated by MTU discovery}\n"); p2a(tcps_sndacks, tcps_delack, "\t\t{:sent-ack-only-packets/%ju} " "{N:/ack-only packet%s/} ({:sent-packets-delayed/%ju} " "{N:delayed})\n"); p(tcps_sndurg, "\t\t{:sent-urg-only-packets/%ju} " "{N:/URG only packet%s}\n"); p(tcps_sndprobe, "\t\t{:sent-window-probe-packets/%ju} " "{N:/window probe packet%s}\n"); p(tcps_sndwinup, "\t\t{:sent-window-update-packets/%ju} " "{N:/window update packet%s}\n"); p(tcps_sndctrl, "\t\t{:sent-control-packets/%ju} " "{N:/control packet%s}\n"); p(tcps_rcvtotal, "\t{:received-packets/%ju} " "{N:/packet%s received}\n"); p2(tcps_rcvackpack, tcps_rcvackbyte, "\t\t" "{:received-ack-packets/%ju} {N:/ack%s} " "{N:(for} {:received-ack-bytes/%ju} {N:/byte%s})\n"); p(tcps_rcvdupack, "\t\t{:received-duplicate-acks/%ju} " "{N:/duplicate ack%s}\n"); p(tcps_tunneled_pkts, "\t\t{:received-udp-tunneled-pkts/%ju} " "{N:/UDP tunneled pkt%s}\n"); p(tcps_tunneled_errs, "\t\t{:received-bad-udp-tunneled-pkts/%ju} " "{N:/UDP tunneled pkt cnt with error%s}\n"); p(tcps_rcvacktoomuch, "\t\t{:received-acks-for-unsent-data/%ju} " "{N:/ack%s for unsent data}\n"); p2(tcps_rcvpack, tcps_rcvbyte, "\t\t" "{:received-in-sequence-packets/%ju} {N:/packet%s} " "({:received-in-sequence-bytes/%ju} {N:/byte%s}) " "{N:received in-sequence}\n"); p2(tcps_rcvduppack, tcps_rcvdupbyte, "\t\t" "{:received-completely-duplicate-packets/%ju} " "{N:/completely duplicate packet%s} " "({:received-completely-duplicate-bytes/%ju} {N:/byte%s})\n"); p(tcps_pawsdrop, "\t\t{:received-old-duplicate-packets/%ju} " "{N:/old duplicate packet%s}\n"); p2(tcps_rcvpartduppack, tcps_rcvpartdupbyte, "\t\t" "{:received-some-duplicate-packets/%ju} " "{N:/packet%s with some dup. data} " "({:received-some-duplicate-bytes/%ju} {N:/byte%s duped/})\n"); p2(tcps_rcvoopack, tcps_rcvoobyte, "\t\t{:received-out-of-order/%ju} " "{N:/out-of-order packet%s} " "({:received-out-of-order-bytes/%ju} {N:/byte%s})\n"); p2(tcps_rcvpackafterwin, tcps_rcvbyteafterwin, "\t\t" "{:received-after-window-packets/%ju} {N:/packet%s} " "({:received-after-window-bytes/%ju} {N:/byte%s}) " "{N:of data after window}\n"); p(tcps_rcvwinprobe, "\t\t{:received-window-probes/%ju} " "{N:/window probe%s}\n"); p(tcps_rcvwinupd, "\t\t{:receive-window-update-packets/%ju} " "{N:/window update packet%s}\n"); p(tcps_dsack_count, "\t\t{:received-with-dsack-packets/%ju} " "{N:/packet%s received with dsack}\n"); p(tcps_dsack_bytes, "\t\t{:received-with-dsack-bytes/%ju} " "{N:/dsack byte%s received (no TLP involved)}\n"); p(tcps_dsack_tlp_bytes, "\t\t{:received-with-dsack-bytes-tlp/%ju} " "{N:/dsack byte%s received (TLP responsible)}\n"); p(tcps_rcvafterclose, "\t\t{:received-after-close-packets/%ju} " "{N:/packet%s received after close}\n"); p(tcps_rcvbadsum, "\t\t{:discard-bad-checksum/%ju} " "{N:/discarded for bad checksum%s}\n"); p(tcps_rcvbadoff, "\t\t{:discard-bad-header-offset/%ju} " "{N:/discarded for bad header offset field%s}\n"); p1a(tcps_rcvshort, "\t\t{:discard-too-short/%ju} " "{N:discarded because packet too short}\n"); p1a(tcps_rcvreassfull, "\t\t{:discard-reassembly-queue-full/%ju} " "{N:discarded due to full reassembly queue}\n"); p(tcps_connattempt, "\t{:connection-requests/%ju} " "{N:/connection request%s}\n"); p(tcps_accepts, "\t{:connections-accepts/%ju} " "{N:/connection accept%s}\n"); p(tcps_badsyn, "\t{:bad-connection-attempts/%ju} " "{N:/bad connection attempt%s}\n"); p(tcps_listendrop, "\t{:listen-queue-overflows/%ju} " "{N:/listen queue overflow%s}\n"); p(tcps_badrst, "\t{:ignored-in-window-resets/%ju} " "{N:/ignored RSTs in the window%s}\n"); p(tcps_connects, "\t{:connections-established/%ju} " "{N:/connection%s established (including accepts)}\n"); p(tcps_usedrtt, "\t\t{:connections-hostcache-rtt/%ju} " "{N:/time%s used RTT from hostcache}\n"); p(tcps_usedrttvar, "\t\t{:connections-hostcache-rttvar/%ju} " "{N:/time%s used RTT variance from hostcache}\n"); p(tcps_usedssthresh, "\t\t{:connections-hostcache-ssthresh/%ju} " "{N:/time%s used slow-start threshold from hostcache}\n"); p2(tcps_closed, tcps_drops, "\t{:connections-closed/%ju} " "{N:/connection%s closed (including} " "{:connection-drops/%ju} {N:/drop%s})\n"); p(tcps_cachedrtt, "\t\t{:connections-updated-rtt-on-close/%ju} " "{N:/connection%s updated cached RTT on close}\n"); p(tcps_cachedrttvar, "\t\t" "{:connections-updated-variance-on-close/%ju} " "{N:/connection%s updated cached RTT variance on close}\n"); p(tcps_cachedssthresh, "\t\t" "{:connections-updated-ssthresh-on-close/%ju} " "{N:/connection%s updated cached ssthresh on close}\n"); p(tcps_conndrops, "\t{:embryonic-connections-dropped/%ju} " "{N:/embryonic connection%s dropped}\n"); p2(tcps_rttupdated, tcps_segstimed, "\t{:segments-updated-rtt/%ju} " "{N:/segment%s updated rtt (of} " "{:segment-update-attempts/%ju} {N:/attempt%s})\n"); p(tcps_rexmttimeo, "\t{:retransmit-timeouts/%ju} " "{N:/retransmit timeout%s}\n"); p(tcps_timeoutdrop, "\t\t" "{:connections-dropped-by-retransmit-timeout/%ju} " "{N:/connection%s dropped by rexmit timeout}\n"); p(tcps_persisttimeo, "\t{:persist-timeout/%ju} " "{N:/persist timeout%s}\n"); p(tcps_persistdrop, "\t\t" "{:connections-dropped-by-persist-timeout/%ju} " "{N:/connection%s dropped by persist timeout}\n"); p(tcps_finwait2_drops, "\t" "{:connections-dropped-by-finwait2-timeout/%ju} " "{N:/Connection%s (fin_wait_2) dropped because of timeout}\n"); p(tcps_keeptimeo, "\t{:keepalive-timeout/%ju} " "{N:/keepalive timeout%s}\n"); p(tcps_keepprobe, "\t\t{:keepalive-probes/%ju} " "{N:/keepalive probe%s sent}\n"); p(tcps_keepdrops, "\t\t{:connections-dropped-by-keepalives/%ju} " "{N:/connection%s dropped by keepalive}\n"); p(tcps_progdrops, "\t{:connections-dropped-due-to-progress-time/%ju} " "{N:/connection%s dropped due to exceeding progress time}\n"); p(tcps_predack, "\t{:ack-header-predictions/%ju} " "{N:/correct ACK header prediction%s}\n"); p(tcps_preddat, "\t{:data-packet-header-predictions/%ju} " "{N:/correct data packet header prediction%s}\n"); xo_open_container("syncache"); p3(tcps_sc_added, "\t{:entries-added/%ju} " "{N:/syncache entr%s added}\n"); p1a(tcps_sc_retransmitted, "\t\t{:retransmitted/%ju} " "{N:/retransmitted}\n"); p1a(tcps_sc_dupsyn, "\t\t{:duplicates/%ju} {N:/dupsyn}\n"); p1a(tcps_sc_dropped, "\t\t{:dropped/%ju} {N:/dropped}\n"); p1a(tcps_sc_completed, "\t\t{:completed/%ju} {N:/completed}\n"); p1a(tcps_sc_bucketoverflow, "\t\t{:bucket-overflow/%ju} " "{N:/bucket overflow}\n"); p1a(tcps_sc_cacheoverflow, "\t\t{:cache-overflow/%ju} " "{N:/cache overflow}\n"); p1a(tcps_sc_reset, "\t\t{:reset/%ju} {N:/reset}\n"); p1a(tcps_sc_stale, "\t\t{:stale/%ju} {N:/stale}\n"); p1a(tcps_sc_aborted, "\t\t{:aborted/%ju} {N:/aborted}\n"); p1a(tcps_sc_badack, "\t\t{:bad-ack/%ju} {N:/badack}\n"); p1a(tcps_sc_unreach, "\t\t{:unreachable/%ju} {N:/unreach}\n"); p(tcps_sc_zonefail, "\t\t{:zone-failures/%ju} {N:/zone failure%s}\n"); p(tcps_sc_sendcookie, "\t{:sent-cookies/%ju} {N:/cookie%s sent}\n"); p(tcps_sc_recvcookie, "\t{:receivd-cookies/%ju} " "{N:/cookie%s received}\n"); xo_close_container("syncache"); xo_open_container("hostcache"); p3(tcps_hc_added, "\t{:entries-added/%ju} " "{N:/hostcache entr%s added}\n"); p1a(tcps_hc_bucketoverflow, "\t\t{:buffer-overflows/%ju} " "{N:/bucket overflow}\n"); xo_close_container("hostcache"); xo_open_container("sack"); p(tcps_sack_recovery_episode, "\t{:recovery-episodes/%ju} " "{N:/SACK recovery episode%s}\n"); p(tcps_sack_rexmits, "\t{:segment-retransmits/%ju} " "{N:/segment rexmit%s in SACK recovery episodes}\n"); p(tcps_sack_rexmit_bytes, "\t{:byte-retransmits/%ju} " "{N:/byte rexmit%s in SACK recovery episodes}\n"); p(tcps_sack_rcv_blocks, "\t{:received-blocks/%ju} " "{N:/SACK option%s (SACK blocks) received}\n"); p(tcps_sack_send_blocks, "\t{:sent-option-blocks/%ju} " "{N:/SACK option%s (SACK blocks) sent}\n"); p(tcps_sack_lostrexmt, "\t{:lost-retransmissions/%ju} " "{N:/SACK retransmission%s lost}\n"); p1a(tcps_sack_sboverflow, "\t{:scoreboard-overflows/%ju} " "{N:/SACK scoreboard overflow}\n"); xo_close_container("sack"); xo_open_container("ecn"); p(tcps_ecn_rcvce, "\t{:received-ce-packets/%ju} " "{N:/packet%s received with ECN CE bit set}\n"); p(tcps_ecn_rcvect0, "\t{:received-ect0-packets/%ju} " "{N:/packet%s received with ECN ECT(0) bit set}\n"); p(tcps_ecn_rcvect1, "\t{:received-ect1-packets/%ju} " "{N:/packet%s received with ECN ECT(1) bit set}\n"); p(tcps_ecn_sndect0, "\t{:sent-ect0-packets/%ju} " "{N:/packet%s sent with ECN ECT(0) bit set}\n"); p(tcps_ecn_sndect1, "\t{:sent-ect1-packets/%ju} " "{N:/packet%s sent with ECN ECT(1) bit set}\n"); p(tcps_ecn_shs, "\t{:handshakes/%ju} " "{N:/successful ECN handshake%s}\n"); p(tcps_ecn_rcwnd, "\t{:congestion-reductions/%ju} " "{N:/time%s ECN reduced the congestion window}\n"); p(tcps_ace_nect, "\t{:ace-nonect-syn/%ju} " "{N:/ACE SYN packet%s with Non-ECT}\n"); p(tcps_ace_ect0, "\t{:ace-ect0-syn/%ju} " "{N:/ACE SYN packet%s with ECT0}\n"); p(tcps_ace_ect1, "\t{:ace-ect1-syn/%ju} " "{N:/ACE SYN packet%s with ECT1}\n"); p(tcps_ace_ce, "\t{:ace-ce-syn/%ju} " "{N:/ACE SYN packet%s with CE}\n"); xo_close_container("ecn"); xo_open_container("tcp-signature"); p(tcps_sig_rcvgoodsig, "\t{:received-good-signature/%ju} " "{N:/packet%s with matching signature received}\n"); p(tcps_sig_rcvbadsig, "\t{:received-bad-signature/%ju} " "{N:/packet%s with bad signature received}\n"); p(tcps_sig_err_buildsig, "\t{:failed-make-signature/%ju} " "{N:/time%s failed to make signature due to no SA}\n"); p(tcps_sig_err_sigopt, "\t{:no-signature-expected/%ju} " "{N:/time%s unexpected signature received}\n"); p(tcps_sig_err_nosigopt, "\t{:no-signature-provided/%ju} " "{N:/time%s no signature provided by segment}\n"); xo_close_container("tcp-signature"); xo_open_container("pmtud"); p(tcps_pmtud_blackhole_activated, "\t{:pmtud-activated/%ju} " "{N:/Path MTU discovery black hole detection activation%s}\n"); p(tcps_pmtud_blackhole_activated_min_mss, "\t{:pmtud-activated-min-mss/%ju} " "{N:/Path MTU discovery black hole detection min MSS activation%s}\n"); p(tcps_pmtud_blackhole_failed, "\t{:pmtud-failed/%ju} " "{N:/Path MTU discovery black hole detection failure%s}\n"); xo_close_container("pmtud"); xo_open_container("tw"); p(tcps_tw_responds, "\t{:tw_responds/%ju} " "{N:/time%s connection in TIME-WAIT responded with ACK}\n"); p(tcps_tw_recycles, "\t{:tw_recycles/%ju} " "{N:/time%s connection in TIME-WAIT was actively recycled}\n"); p(tcps_tw_resets, "\t{:tw_resets/%ju} " "{N:/time%s connection in TIME-WAIT responded with RST}\n"); xo_close_container("tw"); #undef p #undef p1a #undef p2 #undef p2a #undef p3 xo_open_container("TCP connection count by state"); xo_emit("{T:/TCP connection count by state}:\n"); for (int i = 0; i < TCP_NSTATES; i++) { /* * XXXGL: is there a way in libxo to use %s * in the "content string" of a format * string? I failed to do that, that's why * a temporary buffer is used to construct * format string for xo_emit(). */ char fmtbuf[80]; if (sflag > 1 && tcps_states[i] == 0) continue; snprintf(fmtbuf, sizeof(fmtbuf), "\t{:%s/%%ju} " "{Np:/connection ,connections} in %s state\n", tcpstates[i], tcpstates[i]); xo_emit(fmtbuf, (uintmax_t )tcps_states[i]); } xo_close_container("TCP connection count by state"); xo_close_container("tcp"); } /* * Dump UDP statistics structure. */ void udp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct udpstat udpstat; uint64_t delivered; #ifdef INET6 if (udp_done != 0) return; else udp_done = 1; #endif if (fetch_stats("net.inet.udp.stats", off, &udpstat, sizeof(udpstat), kread_counters) != 0) return; xo_open_container("udp"); xo_emit("{T:/%s}:\n", name); #define p(f, m) if (udpstat.f || sflag <= 1) \ xo_emit("\t" m, (uintmax_t)udpstat.f, plural(udpstat.f)) #define p1a(f, m) if (udpstat.f || sflag <= 1) \ xo_emit("\t" m, (uintmax_t)udpstat.f) p(udps_ipackets, "{:received-datagrams/%ju} " "{N:/datagram%s received}\n"); p1a(udps_hdrops, "{:dropped-incomplete-headers/%ju} " "{N:/with incomplete header}\n"); p1a(udps_badlen, "{:dropped-bad-data-length/%ju} " "{N:/with bad data length field}\n"); p1a(udps_badsum, "{:dropped-bad-checksum/%ju} " "{N:/with bad checksum}\n"); p1a(udps_nosum, "{:dropped-no-checksum/%ju} " "{N:/with no checksum}\n"); p1a(udps_noport, "{:dropped-no-socket/%ju} " "{N:/dropped due to no socket}\n"); p(udps_noportbcast, "{:dropped-broadcast-multicast/%ju} " "{N:/broadcast\\/multicast datagram%s undelivered}\n"); p1a(udps_fullsock, "{:dropped-full-socket-buffer/%ju} " "{N:/dropped due to full socket buffers}\n"); p1a(udpps_pcbhashmiss, "{:not-for-hashed-pcb/%ju} " "{N:/not for hashed pcb}\n"); delivered = udpstat.udps_ipackets - udpstat.udps_hdrops - udpstat.udps_badlen - udpstat.udps_badsum - udpstat.udps_noport - udpstat.udps_noportbcast - udpstat.udps_fullsock; if (delivered || sflag <= 1) xo_emit("\t{:delivered-packets/%ju} {N:/delivered}\n", (uint64_t)delivered); p(udps_opackets, "{:output-packets/%ju} {N:/datagram%s output}\n"); /* the next statistic is cumulative in udps_noportbcast */ p(udps_filtermcast, "{:multicast-source-filter-matches/%ju} " "{N:/time%s multicast source filter matched}\n"); #undef p #undef p1a xo_close_container("udp"); } /* * Dump CARP statistics structure. */ void carp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct carpstats carpstat; if (fetch_stats("net.inet.carp.stats", off, &carpstat, sizeof(carpstat), kread_counters) != 0) return; xo_open_container(name); xo_emit("{T:/%s}:\n", name); #define p(f, m) if (carpstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)carpstat.f, plural(carpstat.f)) #define p2(f, m) if (carpstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)carpstat.f) p(carps_ipackets, "\t{:received-inet-packets/%ju} " "{N:/packet%s received (IPv4)}\n"); p(carps_ipackets6, "\t{:received-inet6-packets/%ju} " "{N:/packet%s received (IPv6)}\n"); p(carps_badttl, "\t\t{:dropped-wrong-ttl/%ju} " "{N:/packet%s discarded for wrong TTL}\n"); p(carps_hdrops, "\t\t{:dropped-short-header/%ju} " "{N:/packet%s shorter than header}\n"); p(carps_badsum, "\t\t{:dropped-bad-checksum/%ju} " "{N:/discarded for bad checksum%s}\n"); p(carps_badver, "\t\t{:dropped-bad-version/%ju} " "{N:/discarded packet%s with a bad version}\n"); p2(carps_badlen, "\t\t{:dropped-short-packet/%ju} " "{N:/discarded because packet too short}\n"); p2(carps_badauth, "\t\t{:dropped-bad-authentication/%ju} " "{N:/discarded for bad authentication}\n"); p2(carps_badvhid, "\t\t{:dropped-bad-vhid/%ju} " "{N:/discarded for bad vhid}\n"); p2(carps_badaddrs, "\t\t{:dropped-bad-address-list/%ju} " "{N:/discarded because of a bad address list}\n"); p(carps_opackets, "\t{:sent-inet-packets/%ju} " "{N:/packet%s sent (IPv4)}\n"); p(carps_opackets6, "\t{:sent-inet6-packets/%ju} " "{N:/packet%s sent (IPv6)}\n"); p2(carps_onomem, "\t\t{:send-failed-memory-error/%ju} " "{N:/send failed due to mbuf memory error}\n"); #if notyet p(carps_ostates, "\t\t{:send-state-updates/%s} " "{N:/state update%s sent}\n"); #endif #undef p #undef p2 xo_close_container(name); } /* * Dump IP statistics structure. */ void ip_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct ipstat ipstat; if (fetch_stats("net.inet.ip.stats", off, &ipstat, sizeof(ipstat), kread_counters) != 0) return; xo_open_container(name); xo_emit("{T:/%s}:\n", name); #define p(f, m) if (ipstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t )ipstat.f, plural(ipstat.f)) #define p1a(f, m) if (ipstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t )ipstat.f) p(ips_total, "\t{:received-packets/%ju} " "{N:/total packet%s received}\n"); p(ips_badsum, "\t{:dropped-bad-checksum/%ju} " "{N:/bad header checksum%s}\n"); p1a(ips_toosmall, "\t{:dropped-below-minimum-size/%ju} " "{N:/with size smaller than minimum}\n"); p1a(ips_tooshort, "\t{:dropped-short-packets/%ju} " "{N:/with data size < data length}\n"); p1a(ips_toolong, "\t{:dropped-too-long/%ju} " "{N:/with ip length > max ip packet size}\n"); p1a(ips_badhlen, "\t{:dropped-short-header-length/%ju} " "{N:/with header length < data size}\n"); p1a(ips_badlen, "\t{:dropped-short-data/%ju} " "{N:/with data length < header length}\n"); p1a(ips_badoptions, "\t{:dropped-bad-options/%ju} " "{N:/with bad options}\n"); p1a(ips_badvers, "\t{:dropped-bad-version/%ju} " "{N:/with incorrect version number}\n"); p(ips_fragments, "\t{:received-fragments/%ju} " "{N:/fragment%s received}\n"); p(ips_fragdropped, "\t{:dropped-fragments/%ju} " "{N:/fragment%s dropped (dup or out of space)}\n"); p(ips_fragtimeout, "\t{:dropped-fragments-after-timeout/%ju} " "{N:/fragment%s dropped after timeout}\n"); p(ips_reassembled, "\t{:reassembled-packets/%ju} " "{N:/packet%s reassembled ok}\n"); p(ips_delivered, "\t{:received-local-packets/%ju} " "{N:/packet%s for this host}\n"); p(ips_noproto, "\t{:dropped-unknown-protocol/%ju} " "{N:/packet%s for unknown\\/unsupported protocol}\n"); p(ips_forward, "\t{:forwarded-packets/%ju} " "{N:/packet%s forwarded}"); p(ips_fastforward, " ({:fast-forwarded-packets/%ju} " "{N:/packet%s fast forwarded})"); if (ipstat.ips_forward || sflag <= 1) xo_emit("\n"); p(ips_cantforward, "\t{:packets-cannot-forward/%ju} " "{N:/packet%s not forwardable}\n"); p(ips_notmember, "\t{:received-unknown-multicast-group/%ju} " "{N:/packet%s received for unknown multicast group}\n"); p(ips_redirectsent, "\t{:redirects-sent/%ju} " "{N:/redirect%s sent}\n"); p(ips_localout, "\t{:sent-packets/%ju} " "{N:/packet%s sent from this host}\n"); p(ips_rawout, "\t{:send-packets-fabricated-header/%ju} " "{N:/packet%s sent with fabricated ip header}\n"); p(ips_odropped, "\t{:discard-no-mbufs/%ju} " "{N:/output packet%s dropped due to no bufs, etc.}\n"); p(ips_noroute, "\t{:discard-no-route/%ju} " "{N:/output packet%s discarded due to no route}\n"); p(ips_fragmented, "\t{:sent-fragments/%ju} " "{N:/output datagram%s fragmented}\n"); p(ips_ofragments, "\t{:fragments-created/%ju} " "{N:/fragment%s created}\n"); p(ips_cantfrag, "\t{:discard-cannot-fragment/%ju} " "{N:/datagram%s that can't be fragmented}\n"); p(ips_nogif, "\t{:discard-tunnel-no-gif/%ju} " "{N:/tunneling packet%s that can't find gif}\n"); p(ips_badaddr, "\t{:discard-bad-address/%ju} " "{N:/datagram%s with bad address in header}\n"); #undef p #undef p1a xo_close_container(name); } /* * Dump ARP statistics structure. */ void arp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct arpstat arpstat; if (fetch_stats("net.link.ether.arp.stats", off, &arpstat, sizeof(arpstat), kread_counters) != 0) return; xo_open_container(name); xo_emit("{T:/%s}:\n", name); #define p(f, m) if (arpstat.f || sflag <= 1) \ xo_emit("\t" m, (uintmax_t)arpstat.f, plural(arpstat.f)) #define p2(f, m) if (arpstat.f || sflag <= 1) \ xo_emit("\t" m, (uintmax_t)arpstat.f, pluralies(arpstat.f)) p(txrequests, "{:sent-requests/%ju} {N:/ARP request%s sent}\n"); p(txerrors, "{:sent-failures/%ju} {N:/ARP request%s failed to sent}\n"); p2(txreplies, "{:sent-replies/%ju} {N:/ARP repl%s sent}\n"); p(rxrequests, "{:received-requests/%ju} " "{N:/ARP request%s received}\n"); p2(rxreplies, "{:received-replies/%ju} " "{N:/ARP repl%s received}\n"); p(received, "{:received-packets/%ju} " "{N:/ARP packet%s received}\n"); p(dropped, "{:dropped-no-entry/%ju} " "{N:/total packet%s dropped due to no ARP entry}\n"); p(timeouts, "{:entries-timeout/%ju} " "{N:/ARP entry%s timed out}\n"); p(dupips, "{:dropped-duplicate-address/%ju} " "{N:/Duplicate IP%s seen}\n"); #undef p #undef p2 xo_close_container(name); } static const char *icmpnames[ICMP_MAXTYPE + 1] = { "echo reply", /* RFC 792 */ "#1", "#2", "destination unreachable", /* RFC 792 */ "source quench", /* RFC 792 */ "routing redirect", /* RFC 792 */ "#6", "#7", "echo", /* RFC 792 */ "router advertisement", /* RFC 1256 */ "router solicitation", /* RFC 1256 */ "time exceeded", /* RFC 792 */ "parameter problem", /* RFC 792 */ "time stamp", /* RFC 792 */ "time stamp reply", /* RFC 792 */ "information request", /* RFC 792 */ "information request reply", /* RFC 792 */ "address mask request", /* RFC 950 */ "address mask reply", /* RFC 950 */ "#19", "#20", "#21", "#22", "#23", "#24", "#25", "#26", "#27", "#28", "#29", "icmp traceroute", /* RFC 1393 */ "datagram conversion error", /* RFC 1475 */ "mobile host redirect", "IPv6 where-are-you", "IPv6 i-am-here", "mobile registration req", "mobile registration reply", "domain name request", /* RFC 1788 */ "domain name reply", /* RFC 1788 */ "icmp SKIP", "icmp photuris", /* RFC 2521 */ }; /* * Dump ICMP statistics. */ void icmp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct icmpstat icmpstat; size_t len; int i, first; if (fetch_stats("net.inet.icmp.stats", off, &icmpstat, sizeof(icmpstat), kread_counters) != 0) return; xo_open_container(name); xo_emit("{T:/%s}:\n", name); #define p(f, m) if (icmpstat.f || sflag <= 1) \ xo_emit(m, icmpstat.f, plural(icmpstat.f)) #define p1a(f, m) if (icmpstat.f || sflag <= 1) \ xo_emit(m, icmpstat.f) #define p2(f, m) if (icmpstat.f || sflag <= 1) \ xo_emit(m, icmpstat.f, plurales(icmpstat.f)) p(icps_error, "\t{:icmp-calls/%lu} " "{N:/call%s to icmp_error}\n"); p(icps_oldicmp, "\t{:errors-not-from-message/%lu} " "{N:/error%s not generated in response to an icmp message}\n"); for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++) { if (icmpstat.icps_outhist[i] != 0) { if (first) { xo_open_list("output-histogram"); xo_emit("\tOutput histogram:\n"); first = 0; } xo_open_instance("output-histogram"); if (icmpnames[i] != NULL) xo_emit("\t\t{k:name/%s}: {:count/%lu}\n", icmpnames[i], icmpstat.icps_outhist[i]); else xo_emit("\t\tunknown ICMP #{k:name/%d}: " "{:count/%lu}\n", i, icmpstat.icps_outhist[i]); xo_close_instance("output-histogram"); } } if (!first) xo_close_list("output-histogram"); p(icps_badcode, "\t{:dropped-bad-code/%lu} " "{N:/message%s with bad code fields}\n"); p(icps_tooshort, "\t{:dropped-too-short/%lu} " "{N:/message%s less than the minimum length}\n"); p(icps_checksum, "\t{:dropped-bad-checksum/%lu} " "{N:/message%s with bad checksum}\n"); p(icps_badlen, "\t{:dropped-bad-length/%lu} " "{N:/message%s with bad length}\n"); p1a(icps_bmcastecho, "\t{:dropped-multicast-echo/%lu} " "{N:/multicast echo requests ignored}\n"); p1a(icps_bmcasttstamp, "\t{:dropped-multicast-timestamp/%lu} " "{N:/multicast timestamp requests ignored}\n"); for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++) { if (icmpstat.icps_inhist[i] != 0) { if (first) { xo_open_list("input-histogram"); xo_emit("\tInput histogram:\n"); first = 0; } xo_open_instance("input-histogram"); if (icmpnames[i] != NULL) xo_emit("\t\t{k:name/%s}: {:count/%lu}\n", icmpnames[i], icmpstat.icps_inhist[i]); else xo_emit( "\t\tunknown ICMP #{k:name/%d}: {:count/%lu}\n", i, icmpstat.icps_inhist[i]); xo_close_instance("input-histogram"); } } if (!first) xo_close_list("input-histogram"); p(icps_reflect, "\t{:sent-packets/%lu} " "{N:/message response%s generated}\n"); p2(icps_badaddr, "\t{:discard-invalid-return-address/%lu} " "{N:/invalid return address%s}\n"); p(icps_noroute, "\t{:discard-no-route/%lu} " "{N:/no return route%s}\n"); #undef p #undef p1a #undef p2 if (live) { len = sizeof i; if (sysctlbyname("net.inet.icmp.maskrepl", &i, &len, NULL, 0) < 0) return; xo_emit("\tICMP address mask responses are " "{q:icmp-address-responses/%sabled}\n", i ? "en" : "dis"); } xo_close_container(name); } /* * Dump IGMP statistics structure. */ void igmp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct igmpstat igmpstat; int error, zflag0; if (fetch_stats("net.inet.igmp.stats", 0, &igmpstat, sizeof(igmpstat), kread) != 0) return; /* * Reread net.inet.igmp.stats when zflag == 1. * This is because this MIB contains version number and * length of the structure which are not set when clearing * the counters. */ zflag0 = zflag; if (zflag) { zflag = 0; error = fetch_stats("net.inet.igmp.stats", 0, &igmpstat, sizeof(igmpstat), kread); zflag = zflag0; if (error) return; } if (igmpstat.igps_version != IGPS_VERSION_3) { xo_warnx("%s: version mismatch (%d != %d)", __func__, igmpstat.igps_version, IGPS_VERSION_3); return; } if (igmpstat.igps_len != IGPS_VERSION3_LEN) { xo_warnx("%s: size mismatch (%d != %d)", __func__, igmpstat.igps_len, IGPS_VERSION3_LEN); return; } xo_open_container(name); xo_emit("{T:/%s}:\n", name); #define p64(f, m) if (igmpstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t) igmpstat.f, plural(igmpstat.f)) #define py64(f, m) if (igmpstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t) igmpstat.f, pluralies(igmpstat.f)) p64(igps_rcv_total, "\t{:received-messages/%ju} " "{N:/message%s received}\n"); p64(igps_rcv_tooshort, "\t{:dropped-too-short/%ju} " "{N:/message%s received with too few bytes}\n"); p64(igps_rcv_badttl, "\t{:dropped-wrong-ttl/%ju} " "{N:/message%s received with wrong TTL}\n"); p64(igps_rcv_badsum, "\t{:dropped-bad-checksum/%ju} " "{N:/message%s received with bad checksum}\n"); py64(igps_rcv_v1v2_queries, "\t{:received-membership-queries/%ju} " "{N:/V1\\/V2 membership quer%s received}\n"); py64(igps_rcv_v3_queries, "\t{:received-v3-membership-queries/%ju} " "{N:/V3 membership quer%s received}\n"); py64(igps_rcv_badqueries, "\t{:dropped-membership-queries/%ju} " "{N:/membership quer%s received with invalid field(s)}\n"); py64(igps_rcv_gen_queries, "\t{:received-general-queries/%ju} " "{N:/general quer%s received}\n"); py64(igps_rcv_group_queries, "\t{:received-group-queries/%ju} " "{N:/group quer%s received}\n"); py64(igps_rcv_gsr_queries, "\t{:received-group-source-queries/%ju} " "{N:/group-source quer%s received}\n"); py64(igps_drop_gsr_queries, "\t{:dropped-group-source-queries/%ju} " "{N:/group-source quer%s dropped}\n"); p64(igps_rcv_reports, "\t{:received-membership-requests/%ju} " "{N:/membership report%s received}\n"); p64(igps_rcv_badreports, "\t{:dropped-membership-reports/%ju} " "{N:/membership report%s received with invalid field(s)}\n"); p64(igps_rcv_ourreports, "\t" "{:received-membership-reports-matching/%ju} " "{N:/membership report%s received for groups to which we belong}" "\n"); p64(igps_rcv_nora, "\t{:received-v3-reports-no-router-alert/%ju} " "{N:/V3 report%s received without Router Alert}\n"); p64(igps_snd_reports, "\t{:sent-membership-reports/%ju} " "{N:/membership report%s sent}\n"); #undef p64 #undef py64 xo_close_container(name); } /* * Dump PIM statistics structure. */ void pim_stats(u_long off __unused, const char *name, int af1 __unused, int proto __unused) { struct pimstat pimstat; if (fetch_stats("net.inet.pim.stats", off, &pimstat, sizeof(pimstat), kread_counters) != 0) return; xo_open_container(name); xo_emit("{T:/%s}:\n", name); #define p(f, m) if (pimstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)pimstat.f, plural(pimstat.f)) #define py(f, m) if (pimstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)pimstat.f, pimstat.f != 1 ? "ies" : "y") p(pims_rcv_total_msgs, "\t{:received-messages/%ju} " "{N:/message%s received}\n"); p(pims_rcv_total_bytes, "\t{:received-bytes/%ju} " "{N:/byte%s received}\n"); p(pims_rcv_tooshort, "\t{:dropped-too-short/%ju} " "{N:/message%s received with too few bytes}\n"); p(pims_rcv_badsum, "\t{:dropped-bad-checksum/%ju} " "{N:/message%s received with bad checksum}\n"); p(pims_rcv_badversion, "\t{:dropped-bad-version/%ju} " "{N:/message%s received with bad version}\n"); p(pims_rcv_registers_msgs, "\t{:received-data-register-messages/%ju} " "{N:/data register message%s received}\n"); p(pims_rcv_registers_bytes, "\t{:received-data-register-bytes/%ju} " "{N:/data register byte%s received}\n"); p(pims_rcv_registers_wrongiif, "\t" "{:received-data-register-wrong-interface/%ju} " "{N:/data register message%s received on wrong iif}\n"); p(pims_rcv_badregisters, "\t{:received-bad-registers/%ju} " "{N:/bad register%s received}\n"); p(pims_snd_registers_msgs, "\t{:sent-data-register-messages/%ju} " "{N:/data register message%s sent}\n"); p(pims_snd_registers_bytes, "\t{:sent-data-register-bytes/%ju} " "{N:/data register byte%s sent}\n"); #undef p #undef py xo_close_container(name); } /* * Dump divert(4) statistics structure. */ void divert_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct divstat divstat; if (fetch_stats("net.inet.divert.stats", off, &divstat, sizeof(divstat), kread_counters) != 0) return; xo_open_container(name); xo_emit("{T:/%s}:\n", name); #define p(f, m) if (divstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)divstat.f, plural(divstat.f)) p(div_diverted, "\t{:diverted-packets/%ju} " "{N:/packet%s successfully diverted to userland}\n"); p(div_noport, "\t{:noport-fails/%ju} " "{N:/packet%s failed to divert due to no socket bound at port}\n"); p(div_outbound, "\t{:outbound-packets/%ju} " "{N:/packet%s successfully re-injected as outbound}\n"); p(div_inbound, "\t{:inbound-packets/%ju} " "{N:/packet%s successfully re-injected as inbound}\n"); #undef p xo_close_container(name); } #ifdef INET /* * Pretty print an Internet address (net address + port). */ static void inetprint(const char *container, struct in_addr *in, int port, const char *proto, int num_port, const int af1) { struct servent *sp = 0; char line[80], *cp; int width; size_t alen, plen; if (container) xo_open_container(container); if (Wflag) snprintf(line, sizeof(line), "%s.", inetname(in)); else snprintf(line, sizeof(line), "%.*s.", (Aflag && !num_port) ? 12 : 16, inetname(in)); alen = strlen(line); cp = line + alen; if (!num_port && port) sp = getservbyport((int)port, proto); if (sp || port == 0) snprintf(cp, sizeof(line) - alen, "%.15s ", sp ? sp->s_name : "*"); else snprintf(cp, sizeof(line) - alen, "%d ", ntohs((u_short)port)); width = (Aflag && !Wflag) ? 18 : ((!Wflag || af1 == AF_INET) ? 22 : 45); if (Wflag) xo_emit("{d:target/%-*s} ", width, line); else xo_emit("{d:target/%-*.*s} ", width, width, line); plen = strlen(cp) - 1; alen--; xo_emit("{e:address/%*.*s}{e:port/%*.*s}", alen, alen, line, plen, plen, cp); if (container) xo_close_container(container); } /* * Construct an Internet address representation. * If numeric_addr has been supplied, give * numeric value, otherwise try for symbolic name. */ char * inetname(struct in_addr *inp) { char *cp; static char line[MAXHOSTNAMELEN]; struct hostent *hp; cp = 0; if (!numeric_addr && inp->s_addr != INADDR_ANY) { hp = gethostbyaddr((char *)inp, sizeof (*inp), AF_INET); if (hp) { cp = hp->h_name; trimdomain(cp, strlen(cp)); } } if (inp->s_addr == INADDR_ANY) strcpy(line, "*"); else if (cp) { strlcpy(line, cp, sizeof(line)); } else { inp->s_addr = ntohl(inp->s_addr); #define C(x) ((u_int)((x) & 0xff)) snprintf(line, sizeof(line), "%u.%u.%u.%u", C(inp->s_addr >> 24), C(inp->s_addr >> 16), C(inp->s_addr >> 8), C(inp->s_addr)); } return (line); } #endif diff --git a/usr.bin/netstat/inet6.c b/usr.bin/netstat/inet6.c index 0a19627ba938..3adf5548aea4 100644 --- a/usr.bin/netstat/inet6.c +++ b/usr.bin/netstat/inet6.c @@ -1,1363 +1,1360 @@ /* BSDI inet.c,v 2.3 1995/10/24 02:19:29 prb Exp */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include #ifdef INET6 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" static char ntop_buf[INET6_ADDRSTRLEN]; static const char *ip6nh[] = { "hop by hop", "ICMP", "IGMP", "#3", "IP", "#5", "TCP", "#7", "#8", "#9", "#10", "#11", "#12", "#13", "#14", "#15", "#16", "UDP", "#18", "#19", "#20", "#21", "IDP", "#23", "#24", "#25", "#26", "#27", "#28", "TP", "#30", "#31", "#32", "#33", "#34", "#35", "#36", "#37", "#38", "#39", "#40", "IP6", "#42", "routing", "fragment", "#45", "#46", "#47", "#48", "#49", "ESP", "AH", "#52", "#53", "#54", "#55", "#56", "#57", "ICMP6", "no next header", "destination option", "#61", "mobility", "#63", "#64", "#65", "#66", "#67", "#68", "#69", "#70", "#71", "#72", "#73", "#74", "#75", "#76", "#77", "#78", "#79", "ISOIP", "#81", "#82", "#83", "#84", "#85", "#86", "#87", "#88", "OSPF", "#80", "#91", "#92", "#93", "#94", "#95", "#96", "Ethernet", "#98", "#99", "#100", "#101", "#102", "PIM", "#104", "#105", "#106", "#107", "#108", "#109", "#110", "#111", "#112", "#113", "#114", "#115", "#116", "#117", "#118", "#119", "#120", "#121", "#122", "#123", "#124", "#125", "#126", "#127", "#128", "#129", "#130", "#131", "SCTP", "#133", "#134", "#135", "UDPLite", "#137", "#138", "#139", "#140", "#141", "#142", "#143", "#144", "#145", "#146", "#147", "#148", "#149", "#150", "#151", "#152", "#153", "#154", "#155", "#156", "#157", "#158", "#159", "#160", "#161", "#162", "#163", "#164", "#165", "#166", "#167", "#168", "#169", "#170", "#171", "#172", "#173", "#174", "#175", "#176", "#177", "#178", "#179", "#180", "#181", "#182", "#183", "#184", "#185", "#186", "#187", "#188", "#189", "#180", "#191", "#192", "#193", "#194", "#195", "#196", "#197", "#198", "#199", "#200", "#201", "#202", "#203", "#204", "#205", "#206", "#207", "#208", "#209", "#210", "#211", "#212", "#213", "#214", "#215", "#216", "#217", "#218", "#219", "#220", "#221", "#222", "#223", "#224", "#225", "#226", "#227", "#228", "#229", "#230", "#231", "#232", "#233", "#234", "#235", "#236", "#237", "#238", "#239", "#240", "#241", "#242", "#243", "#244", "#245", "#246", "#247", "#248", "#249", "#250", "#251", "#252", "#253", "#254", "#255", }; static const char *srcrule_str[] = { "first candidate", "same address", "appropriate scope", "deprecated address", "home address", "outgoing interface", "matching label", "public/temporary address", "alive interface", "better virtual status", "preferred source", "rule #11", "rule #12", "rule #13", "longest match", "rule #15", }; /* * Dump IP6 statistics structure. */ void ip6_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct ip6stat ip6stat; int first, i; if (fetch_stats("net.inet6.ip6.stats", off, &ip6stat, sizeof(ip6stat), kread_counters) != 0) return; xo_open_container(name); xo_emit("{T:/%s}:\n", name); #define p(f, m) if (ip6stat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)ip6stat.f, plural(ip6stat.f)) #define p1a(f, m) if (ip6stat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)ip6stat.f) p(ip6s_total, "\t{:received-packets/%ju} " "{N:/total packet%s received}\n"); p1a(ip6s_toosmall, "\t{:dropped-below-minimum-size/%ju} " "{N:/with size smaller than minimum}\n"); p1a(ip6s_tooshort, "\t{:dropped-short-packets/%ju} " "{N:/with data size < data length}\n"); p1a(ip6s_badoptions, "\t{:dropped-bad-options/%ju} " "{N:/with bad options}\n"); p1a(ip6s_badvers, "\t{:dropped-bad-version/%ju} " "{N:/with incorrect version number}\n"); p(ip6s_fragments, "\t{:received-fragments/%ju} " "{N:/fragment%s received}\n"); p(ip6s_fragdropped, "\t{:dropped-fragment/%ju} " "{N:/fragment%s dropped (dup or out of space)}\n"); p(ip6s_fragtimeout, "\t{:dropped-fragment-after-timeout/%ju} " "{N:/fragment%s dropped after timeout}\n"); p(ip6s_fragoverflow, "\t{:dropped-fragments-overflow/%ju} " "{N:/fragment%s that exceeded limit}\n"); p(ip6s_atomicfrags, "\t{:atomic-fragments/%ju} " "{N:/atomic fragment%s}\n"); p(ip6s_reassembled, "\t{:reassembled-packets/%ju} " "{N:/packet%s reassembled ok}\n"); p(ip6s_delivered, "\t{:received-local-packets/%ju} " "{N:/packet%s for this host}\n"); p(ip6s_forward, "\t{:forwarded-packets/%ju} " "{N:/packet%s forwarded}\n"); p(ip6s_cantforward, "\t{:packets-not-forwardable/%ju} " "{N:/packet%s not forwardable}\n"); p(ip6s_redirectsent, "\t{:sent-redirects/%ju} " "{N:/redirect%s sent}\n"); p(ip6s_localout, "\t{:sent-packets/%ju} " "{N:/packet%s sent from this host}\n"); p(ip6s_rawout, "\t{:send-packets-fabricated-header/%ju} " "{N:/packet%s sent with fabricated ip header}\n"); p(ip6s_odropped, "\t{:discard-no-mbufs/%ju} " "{N:/output packet%s dropped due to no bufs, etc.}\n"); p(ip6s_noroute, "\t{:discard-no-route/%ju} " "{N:/output packet%s discarded due to no route}\n"); p(ip6s_fragmented, "\t{:sent-fragments/%ju} " "{N:/output datagram%s fragmented}\n"); p(ip6s_ofragments, "\t{:fragments-created/%ju} " "{N:/fragment%s created}\n"); p(ip6s_cantfrag, "\t{:discard-cannot-fragment/%ju} " "{N:/datagram%s that can't be fragmented}\n"); p(ip6s_badscope, "\t{:discard-scope-violations/%ju} " "{N:/packet%s that violated scope rules}\n"); p(ip6s_notmember, "\t{:multicast-no-join-packets/%ju} " "{N:/multicast packet%s which we don't join}\n"); for (first = 1, i = 0; i < IP6S_HDRCNT; i++) if (ip6stat.ip6s_nxthist[i] != 0) { if (first) { xo_emit("\t{T:Input histogram}:\n"); xo_open_list("input-histogram"); first = 0; } xo_open_instance("input-histogram"); xo_emit("\t\t{k:name/%s}: {:count/%ju}\n", ip6nh[i], (uintmax_t)ip6stat.ip6s_nxthist[i]); xo_close_instance("input-histogram"); } if (!first) xo_close_list("input-histogram"); xo_open_container("mbuf-statistics"); xo_emit("\t{T:Mbuf statistics}:\n"); xo_emit("\t\t{:one-mbuf/%ju} {N:/one mbuf}\n", (uintmax_t)ip6stat.ip6s_m1); for (first = 1, i = 0; i < IP6S_M2MMAX; i++) { char ifbuf[IFNAMSIZ]; if (ip6stat.ip6s_m2m[i] != 0) { if (first) { xo_emit("\t\t{N:two or more mbuf}:\n"); xo_open_list("mbuf-data"); first = 0; } xo_open_instance("mbuf-data"); xo_emit("\t\t\t{k:name/%s}= {:count/%ju}\n", if_indextoname(i, ifbuf), (uintmax_t)ip6stat.ip6s_m2m[i]); xo_close_instance("mbuf-data"); } } if (!first) xo_close_list("mbuf-data"); xo_emit("\t\t{:one-extra-mbuf/%ju} {N:one ext mbuf}\n", (uintmax_t)ip6stat.ip6s_mext1); xo_emit("\t\t{:two-or-more-extra-mbufs/%ju} " "{N:/two or more ext mbuf}\n", (uintmax_t)ip6stat.ip6s_mext2m); xo_close_container("mbuf-statistics"); p(ip6s_exthdrtoolong, "\t{:dropped-header-too-long/%ju} " "{N:/packet%s whose headers are not contiguous}\n"); p(ip6s_nogif, "\t{:discard-tunnel-no-gif/%ju} " "{N:/tunneling packet%s that can't find gif}\n"); p(ip6s_toomanyhdr, "\t{:dropped-too-many-headers/%ju} " "{N:/packet%s discarded because of too many headers}\n"); /* for debugging source address selection */ #define PRINT_SCOPESTAT(s,i) do {\ switch(i) { /* XXX hardcoding in each case */\ case 1:\ p(s, "\t\t{ke:name/interface-locals}{:count/%ju} " \ "{N:/interface-local%s}\n"); \ break;\ case 2:\ p(s,"\t\t{ke:name/link-locals}{:count/%ju} " \ "{N:/link-local%s}\n"); \ break;\ case 5:\ p(s,"\t\t{ke:name/site-locals}{:count/%ju} " \ "{N:/site-local%s}\n");\ break;\ case 14:\ p(s,"\t\t{ke:name/globals}{:count/%ju} " \ "{N:/global%s}\n");\ break;\ default:\ xo_emit("\t\t{qke:name/%#x}{:count/%ju} " \ "{N:/addresses scope=%#x}\n",\ i, (uintmax_t)ip6stat.s, i); \ }\ } while (0); xo_open_container("source-address-selection"); p(ip6s_sources_none, "\t{:address-selection-failures/%ju} " "{N:/failure%s of source address selection}\n"); for (first = 1, i = 0; i < IP6S_SCOPECNT; i++) { if (ip6stat.ip6s_sources_sameif[i]) { if (first) { xo_open_list("outgoing-interface"); xo_emit("\tsource addresses on an outgoing " "I/F\n"); first = 0; } xo_open_instance("outgoing-interface"); PRINT_SCOPESTAT(ip6s_sources_sameif[i], i); xo_close_instance("outgoing-interface"); } } if (!first) xo_close_list("outgoing-interface"); for (first = 1, i = 0; i < IP6S_SCOPECNT; i++) { if (ip6stat.ip6s_sources_otherif[i]) { if (first) { xo_open_list("non-outgoing-interface"); xo_emit("\tsource addresses on a non-outgoing " "I/F\n"); first = 0; } xo_open_instance("non-outgoing-interface"); PRINT_SCOPESTAT(ip6s_sources_otherif[i], i); xo_close_instance("non-outgoing-interface"); } } if (!first) xo_close_list("non-outgoing-interface"); for (first = 1, i = 0; i < IP6S_SCOPECNT; i++) { if (ip6stat.ip6s_sources_samescope[i]) { if (first) { xo_open_list("same-source"); xo_emit("\tsource addresses of same scope\n"); first = 0; } xo_open_instance("same-source"); PRINT_SCOPESTAT(ip6s_sources_samescope[i], i); xo_close_instance("same-source"); } } if (!first) xo_close_list("same-source"); for (first = 1, i = 0; i < IP6S_SCOPECNT; i++) { if (ip6stat.ip6s_sources_otherscope[i]) { if (first) { xo_open_list("different-scope"); xo_emit("\tsource addresses of a different " "scope\n"); first = 0; } xo_open_instance("different-scope"); PRINT_SCOPESTAT(ip6s_sources_otherscope[i], i); xo_close_instance("different-scope"); } } if (!first) xo_close_list("different-scope"); for (first = 1, i = 0; i < IP6S_SCOPECNT; i++) { if (ip6stat.ip6s_sources_deprecated[i]) { if (first) { xo_open_list("deprecated-source"); xo_emit("\tdeprecated source addresses\n"); first = 0; } xo_open_instance("deprecated-source"); PRINT_SCOPESTAT(ip6s_sources_deprecated[i], i); xo_close_instance("deprecated-source"); } } if (!first) xo_close_list("deprecated-source"); for (first = 1, i = 0; i < IP6S_RULESMAX; i++) { if (ip6stat.ip6s_sources_rule[i]) { if (first) { xo_open_list("rules-applied"); xo_emit("\t{T:Source addresses selection " "rule applied}:\n"); first = 0; } xo_open_instance("rules-applied"); xo_emit("\t\t{ke:name/%s}{:count/%ju} {d:name/%s}\n", srcrule_str[i], (uintmax_t)ip6stat.ip6s_sources_rule[i], srcrule_str[i]); xo_close_instance("rules-applied"); } } if (!first) xo_close_list("rules-applied"); xo_close_container("source-address-selection"); #undef p #undef p1a xo_close_container(name); } /* * Dump IPv6 per-interface statistics based on RFC 2465. */ void ip6_ifstats(char *ifname) { struct in6_ifreq ifr; int s; #define p(f, m) if (ifr.ifr_ifru.ifru_stat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)ifr.ifr_ifru.ifru_stat.f, \ plural(ifr.ifr_ifru.ifru_stat.f)) if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { xo_warn("Warning: socket(AF_INET6)"); return; } strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCGIFSTAT_IN6, (char *)&ifr) < 0) { if (errno != EPFNOSUPPORT) xo_warn("Warning: ioctl(SIOCGIFSTAT_IN6)"); goto end; } xo_emit("{T:/ip6 on %s}:\n", ifr.ifr_name); xo_open_instance("ip6-interface-statistics"); xo_emit("{ke:name/%s}", ifr.ifr_name); p(ifs6_in_receive, "\t{:received-packets/%ju} " "{N:/total input datagram%s}\n"); p(ifs6_in_hdrerr, "\t{:dropped-invalid-header/%ju} " "{N:/datagram%s with invalid header received}\n"); p(ifs6_in_toobig, "\t{:dropped-mtu-exceeded/%ju} " "{N:/datagram%s exceeded MTU received}\n"); p(ifs6_in_noroute, "\t{:dropped-no-route/%ju} " "{N:/datagram%s with no route received}\n"); p(ifs6_in_addrerr, "\t{:dropped-invalid-destination/%ju} " "{N:/datagram%s with invalid dst received}\n"); p(ifs6_in_protounknown, "\t{:dropped-unknown-protocol/%ju} " "{N:/datagram%s with unknown proto received}\n"); p(ifs6_in_truncated, "\t{:dropped-truncated/%ju} " "{N:/truncated datagram%s received}\n"); p(ifs6_in_discard, "\t{:dropped-discarded/%ju} " "{N:/input datagram%s discarded}\n"); p(ifs6_in_deliver, "\t{:received-valid-packets/%ju} " "{N:/datagram%s delivered to an upper layer protocol}\n"); p(ifs6_out_forward, "\t{:sent-forwarded/%ju} " "{N:/datagram%s forwarded to this interface}\n"); p(ifs6_out_request, "\t{:sent-packets/%ju} " "{N:/datagram%s sent from an upper layer protocol}\n"); p(ifs6_out_discard, "\t{:discard-packets/%ju} " "{N:/total discarded output datagram%s}\n"); p(ifs6_out_fragok, "\t{:discard-fragments/%ju} " "{N:/output datagram%s fragmented}\n"); p(ifs6_out_fragfail, "\t{:fragments-failed/%ju} " "{N:/output datagram%s failed on fragment}\n"); p(ifs6_out_fragcreat, "\t{:fragments-created/%ju} " "{N:/output datagram%s succeeded on fragment}\n"); p(ifs6_reass_reqd, "\t{:reassembly-required/%ju} " "{N:/incoming datagram%s fragmented}\n"); p(ifs6_reass_ok, "\t{:reassembled-packets/%ju} " "{N:/datagram%s reassembled}\n"); p(ifs6_reass_fail, "\t{:reassembly-failed/%ju} " "{N:/datagram%s failed on reassembly}\n"); p(ifs6_in_mcast, "\t{:received-multicast/%ju} " "{N:/multicast datagram%s received}\n"); p(ifs6_out_mcast, "\t{:sent-multicast/%ju} " "{N:/multicast datagram%s sent}\n"); end: xo_close_instance("ip6-interface-statistics"); close(s); #undef p } static const char *icmp6names[] = { "#0", "unreach", "packet too big", "time exceed", "parameter problem", "#5", "#6", "#7", "#8", "#9", "#10", "#11", "#12", "#13", "#14", "#15", "#16", "#17", "#18", "#19", "#20", "#21", "#22", "#23", "#24", "#25", "#26", "#27", "#28", "#29", "#30", "#31", "#32", "#33", "#34", "#35", "#36", "#37", "#38", "#39", "#40", "#41", "#42", "#43", "#44", "#45", "#46", "#47", "#48", "#49", "#50", "#51", "#52", "#53", "#54", "#55", "#56", "#57", "#58", "#59", "#60", "#61", "#62", "#63", "#64", "#65", "#66", "#67", "#68", "#69", "#70", "#71", "#72", "#73", "#74", "#75", "#76", "#77", "#78", "#79", "#80", "#81", "#82", "#83", "#84", "#85", "#86", "#87", "#88", "#89", "#80", "#91", "#92", "#93", "#94", "#95", "#96", "#97", "#98", "#99", "#100", "#101", "#102", "#103", "#104", "#105", "#106", "#107", "#108", "#109", "#110", "#111", "#112", "#113", "#114", "#115", "#116", "#117", "#118", "#119", "#120", "#121", "#122", "#123", "#124", "#125", "#126", "#127", "echo", "echo reply", "multicast listener query", "MLDv1 listener report", "MLDv1 listener done", "router solicitation", "router advertisement", "neighbor solicitation", "neighbor advertisement", "redirect", "router renumbering", "node information request", "node information reply", "inverse neighbor solicitation", "inverse neighbor advertisement", "MLDv2 listener report", "#144", "#145", "#146", "#147", "#148", "#149", "#150", "#151", "#152", "#153", "#154", "#155", "#156", "#157", "#158", "#159", "#160", "#161", "#162", "#163", "#164", "#165", "#166", "#167", "#168", "#169", "#170", "#171", "#172", "#173", "#174", "#175", "#176", "#177", "#178", "#179", "#180", "#181", "#182", "#183", "#184", "#185", "#186", "#187", "#188", "#189", "#180", "#191", "#192", "#193", "#194", "#195", "#196", "#197", "#198", "#199", "#200", "#201", "#202", "#203", "#204", "#205", "#206", "#207", "#208", "#209", "#210", "#211", "#212", "#213", "#214", "#215", "#216", "#217", "#218", "#219", "#220", "#221", "#222", "#223", "#224", "#225", "#226", "#227", "#228", "#229", "#230", "#231", "#232", "#233", "#234", "#235", "#236", "#237", "#238", "#239", "#240", "#241", "#242", "#243", "#244", "#245", "#246", "#247", "#248", "#249", "#250", "#251", "#252", "#253", "#254", "#255", }; /* * Dump ICMP6 statistics. */ void icmp6_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct icmp6stat icmp6stat; int i, first; if (fetch_stats("net.inet6.icmp6.stats", off, &icmp6stat, sizeof(icmp6stat), kread_counters) != 0) return; xo_emit("{T:/%s}:\n", name); xo_open_container(name); #define p(f, m) if (icmp6stat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)icmp6stat.f, plural(icmp6stat.f)) #define p_5(f, m) if (icmp6stat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)icmp6stat.f) p(icp6s_error, "\t{:icmp6-calls/%ju} " "{N:/call%s to icmp6_error}\n"); p(icp6s_canterror, "\t{:errors-not-generated-from-message/%ju} " "{N:/error%s not generated in response to an icmp6 message}\n"); p(icp6s_toofreq, "\t{:errors-discarded-by-rate-limitation/%ju} " "{N:/error%s not generated because of rate limitation}\n"); #define NELEM (int)(sizeof(icmp6stat.icp6s_outhist)/sizeof(icmp6stat.icp6s_outhist[0])) for (first = 1, i = 0; i < NELEM; i++) if (icmp6stat.icp6s_outhist[i] != 0) { if (first) { xo_open_list("output-histogram"); xo_emit("\t{T:Output histogram}:\n"); first = 0; } xo_open_instance("output-histogram"); xo_emit("\t\t{k:name/%s}: {:count/%ju}\n", icmp6names[i], (uintmax_t)icmp6stat.icp6s_outhist[i]); xo_close_instance("output-histogram"); } if (!first) xo_close_list("output-histogram"); #undef NELEM p(icp6s_badcode, "\t{:dropped-bad-code/%ju} " "{N:/message%s with bad code fields}\n"); p(icp6s_tooshort, "\t{:dropped-too-short/%ju} " "{N:/message%s < minimum length}\n"); p(icp6s_checksum, "\t{:dropped-bad-checksum/%ju} " "{N:/bad checksum%s}\n"); p(icp6s_badlen, "\t{:dropped-bad-length/%ju} " "{N:/message%s with bad length}\n"); p(icp6s_dropped, "\t{:dropped-no-entry/%ju} " "{N:/total packet%s dropped due to failed NDP resolution}\n"); #define NELEM (int)(sizeof(icmp6stat.icp6s_inhist)/sizeof(icmp6stat.icp6s_inhist[0])) for (first = 1, i = 0; i < NELEM; i++) if (icmp6stat.icp6s_inhist[i] != 0) { if (first) { xo_open_list("input-histogram"); xo_emit("\t{T:Input histogram}:\n"); first = 0; } xo_open_instance("input-histogram"); xo_emit("\t\t{k:name/%s}: {:count/%ju}\n", icmp6names[i], (uintmax_t)icmp6stat.icp6s_inhist[i]); xo_close_instance("input-histogram"); } if (!first) xo_close_list("input-histogram"); #undef NELEM xo_emit("\t{T:Histogram of error messages to be generated}:\n"); xo_open_container("errors"); p_5(icp6s_odst_unreach_noroute, "\t\t{:no-route/%ju} " "{N:/no route}\n"); p_5(icp6s_odst_unreach_admin, "\t\t{:admin-prohibited/%ju} " "{N:/administratively prohibited}\n"); p_5(icp6s_odst_unreach_beyondscope, "\t\t{:beyond-scope/%ju} " "{N:/beyond scope}\n"); p_5(icp6s_odst_unreach_addr, "\t\t{:address-unreachable/%ju} " "{N:/address unreachable}\n"); p_5(icp6s_odst_unreach_noport, "\t\t{:port-unreachable/%ju} " "{N:/port unreachable}\n"); p_5(icp6s_opacket_too_big, "\t\t{:packet-too-big/%ju} " "{N:/packet too big}\n"); p_5(icp6s_otime_exceed_transit, "\t\t{:time-exceed-transmit/%ju} " "{N:/time exceed transit}\n"); p_5(icp6s_otime_exceed_reassembly, "\t\t{:time-exceed-reassembly/%ju} " "{N:/time exceed reassembly}\n"); p_5(icp6s_oparamprob_header, "\t\t{:bad-header/%ju} " "{N:/erroneous header field}\n"); p_5(icp6s_oparamprob_nextheader, "\t\t{:bad-next-header/%ju} " "{N:/unrecognized next header}\n"); p_5(icp6s_oparamprob_option, "\t\t{:bad-option/%ju} " "{N:/unrecognized option}\n"); p_5(icp6s_oredirect, "\t\t{:redirects/%ju} " "{N:/redirect}\n"); p_5(icp6s_ounknown, "\t\t{:unknown/%ju} {N:unknown}\n"); p(icp6s_reflect, "\t{:reflect/%ju} " "{N:/message response%s generated}\n"); p(icp6s_nd_toomanyopt, "\t{:too-many-nd-options/%ju} " "{N:/message%s with too many ND options}\n"); p(icp6s_nd_badopt, "\t{:bad-nd-options/%ju} " "{N:/message%s with bad ND options}\n"); p(icp6s_badns, "\t{:bad-neighbor-solicitation/%ju} " "{N:/bad neighbor solicitation message%s}\n"); p(icp6s_badna, "\t{:bad-neighbor-advertisement/%ju} " "{N:/bad neighbor advertisement message%s}\n"); p(icp6s_badrs, "\t{:bad-router-solicitation/%ju} " "{N:/bad router solicitation message%s}\n"); p(icp6s_badra, "\t{:bad-router-advertisement/%ju} " "{N:/bad router advertisement message%s}\n"); p(icp6s_badredirect, "\t{:bad-redirect/%ju} " "{N:/bad redirect message%s}\n"); p(icp6s_overflowdefrtr, "\t{:default-routers-overflows/%ju} " "{N:/default routers overflow%s}\n"); p(icp6s_overflowprfx, "\t{:prefixes-overflows/%ju} " "{N:/prefix overflow%s}\n"); p(icp6s_overflownndp, "\t{:neighbour-entries-overflows/%ju} " "{N:/neighbour entries overflow%s}\n"); p(icp6s_overflowredirect, "\t{:redirect-overflows/%ju} " "{N:/redirect overflow%s}\n"); p(icp6s_invlhlim, "\t{:dropped-invalid-hop-limit/%ju} " "{N:/message%s with invalid hop limit}\n"); xo_close_container("errors"); p(icp6s_pmtuchg, "\t{:path-mtu-changes/%ju} {N:/path MTU change%s}\n"); #undef p #undef p_5 xo_close_container(name); } /* * Dump ICMPv6 per-interface statistics based on RFC 2466. */ void icmp6_ifstats(char *ifname) { struct in6_ifreq ifr; int s; #define p(f, m) if (ifr.ifr_ifru.ifru_icmp6stat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)ifr.ifr_ifru.ifru_icmp6stat.f, \ plural(ifr.ifr_ifru.ifru_icmp6stat.f)) #define p2(f, m) if (ifr.ifr_ifru.ifru_icmp6stat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)ifr.ifr_ifru.ifru_icmp6stat.f, \ pluralies(ifr.ifr_ifru.ifru_icmp6stat.f)) if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { xo_warn("Warning: socket(AF_INET6)"); return; } strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCGIFSTAT_ICMP6, (char *)&ifr) < 0) { if (errno != EPFNOSUPPORT) xo_warn("Warning: ioctl(SIOCGIFSTAT_ICMP6)"); goto end; } xo_emit("{T:/icmp6 on %s}:\n", ifr.ifr_name); xo_open_instance("icmp6-interface-statistics"); xo_emit("{ke:name/%s}", ifr.ifr_name); p(ifs6_in_msg, "\t{:received-packets/%ju} " "{N:/total input message%s}\n"); p(ifs6_in_error, "\t{:received-errors/%ju} " "{N:/total input error message%s}\n"); p(ifs6_in_dstunreach, "\t{:received-destination-unreachable/%ju} " "{N:/input destination unreachable error%s}\n"); p(ifs6_in_adminprohib, "\t{:received-admin-prohibited/%ju} " "{N:/input administratively prohibited error%s}\n"); p(ifs6_in_timeexceed, "\t{:received-time-exceeded/%ju} " "{N:/input time exceeded error%s}\n"); p(ifs6_in_paramprob, "\t{:received-bad-parameter/%ju} " "{N:/input parameter problem error%s}\n"); p(ifs6_in_pkttoobig, "\t{:received-packet-too-big/%ju} " "{N:/input packet too big error%s}\n"); p(ifs6_in_echo, "\t{:received-echo-requests/%ju} " "{N:/input echo request%s}\n"); p2(ifs6_in_echoreply, "\t{:received-echo-replies/%ju} " "{N:/input echo repl%s}\n"); p(ifs6_in_routersolicit, "\t{:received-router-solicitation/%ju} " "{N:/input router solicitation%s}\n"); p(ifs6_in_routeradvert, "\t{:received-router-advertisement/%ju} " "{N:/input router advertisement%s}\n"); p(ifs6_in_neighborsolicit, "\t{:received-neighbor-solicitation/%ju} " "{N:/input neighbor solicitation%s}\n"); p(ifs6_in_neighboradvert, "\t{:received-neighbor-advertisement/%ju} " "{N:/input neighbor advertisement%s}\n"); p(ifs6_in_redirect, "\t{received-redirects/%ju} " "{N:/input redirect%s}\n"); p2(ifs6_in_mldquery, "\t{:received-mld-queries/%ju} " "{N:/input MLD quer%s}\n"); p(ifs6_in_mldreport, "\t{:received-mld-reports/%ju} " "{N:/input MLD report%s}\n"); p(ifs6_in_mlddone, "\t{:received-mld-done/%ju} " "{N:/input MLD done%s}\n"); p(ifs6_out_msg, "\t{:sent-packets/%ju} " "{N:/total output message%s}\n"); p(ifs6_out_error, "\t{:sent-errors/%ju} " "{N:/total output error message%s}\n"); p(ifs6_out_dstunreach, "\t{:sent-destination-unreachable/%ju} " "{N:/output destination unreachable error%s}\n"); p(ifs6_out_adminprohib, "\t{:sent-admin-prohibited/%ju} " "{N:/output administratively prohibited error%s}\n"); p(ifs6_out_timeexceed, "\t{:sent-time-exceeded/%ju} " "{N:/output time exceeded error%s}\n"); p(ifs6_out_paramprob, "\t{:sent-bad-parameter/%ju} " "{N:/output parameter problem error%s}\n"); p(ifs6_out_pkttoobig, "\t{:sent-packet-too-big/%ju} " "{N:/output packet too big error%s}\n"); p(ifs6_out_echo, "\t{:sent-echo-requests/%ju} " "{N:/output echo request%s}\n"); p2(ifs6_out_echoreply, "\t{:sent-echo-replies/%ju} " "{N:/output echo repl%s}\n"); p(ifs6_out_routersolicit, "\t{:sent-router-solicitation/%ju} " "{N:/output router solicitation%s}\n"); p(ifs6_out_routeradvert, "\t{:sent-router-advertisement/%ju} " "{N:/output router advertisement%s}\n"); p(ifs6_out_neighborsolicit, "\t{:sent-neighbor-solicitation/%ju} " "{N:/output neighbor solicitation%s}\n"); p(ifs6_out_neighboradvert, "\t{:sent-neighbor-advertisement/%ju} " "{N:/output neighbor advertisement%s}\n"); p(ifs6_out_redirect, "\t{:sent-redirects/%ju} " "{N:/output redirect%s}\n"); p2(ifs6_out_mldquery, "\t{:sent-mld-queries/%ju} " "{N:/output MLD quer%s}\n"); p(ifs6_out_mldreport, "\t{:sent-mld-reports/%ju} " "{N:/output MLD report%s}\n"); p(ifs6_out_mlddone, "\t{:sent-mld-dones/%ju} " "{N:/output MLD done%s}\n"); end: xo_close_instance("icmp6-interface-statistics"); close(s); #undef p } /* * Dump PIM statistics structure. */ void pim6_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct pim6stat pim6stat; if (fetch_stats("net.inet6.pim.stats", off, &pim6stat, sizeof(pim6stat), kread) != 0) return; xo_emit("{T:/%s}:\n", name); xo_open_container(name); #define p(f, m) if (pim6stat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)pim6stat.f, plural(pim6stat.f)) p(pim6s_rcv_total, "\t{:received-packets/%ju} " "{N:/message%s received}\n"); p(pim6s_rcv_tooshort, "\t{:dropped-too-short/%ju} " "{N:/message%s received with too few bytes}\n"); p(pim6s_rcv_badsum, "\t{:dropped-bad-checksum/%ju} " "{N:/message%s received with bad checksum}\n"); p(pim6s_rcv_badversion, "\t{:dropped-bad-version/%ju} " "{N:/message%s received with bad version}\n"); p(pim6s_rcv_registers, "\t{:received-registers/%ju} " "{N:/register%s received}\n"); p(pim6s_rcv_badregisters, "\t{:received-bad-registers/%ju} " "{N:/bad register%s received}\n"); p(pim6s_snd_registers, "\t{:sent-registers/%ju} " "{N:/register%s sent}\n"); #undef p xo_close_container(name); } /* * Dump raw ip6 statistics structure. */ void rip6_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct rip6stat rip6stat; u_quad_t delivered; if (fetch_stats("net.inet6.ip6.rip6stats", off, &rip6stat, sizeof(rip6stat), kread_counters) != 0) return; xo_emit("{T:/%s}:\n", name); xo_open_container(name); #define p(f, m) if (rip6stat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)rip6stat.f, plural(rip6stat.f)) p(rip6s_ipackets, "\t{:received-packets/%ju} " "{N:/message%s received}\n"); p(rip6s_isum, "\t{:input-checksum-computation/%ju} " "{N:/checksum calculation%s on inbound}\n"); p(rip6s_badsum, "\t{:received-bad-checksum/%ju} " "{N:/message%s with bad checksum}\n"); p(rip6s_nosock, "\t{:dropped-no-socket/%ju} " "{N:/message%s dropped due to no socket}\n"); p(rip6s_nosockmcast, "\t{:dropped-multicast-no-socket/%ju} " "{N:/multicast message%s dropped due to no socket}\n"); p(rip6s_fullsock, "\t{:dropped-full-socket-buffer/%ju} " "{N:/message%s dropped due to full socket buffers}\n"); delivered = rip6stat.rip6s_ipackets - rip6stat.rip6s_badsum - rip6stat.rip6s_nosock - rip6stat.rip6s_nosockmcast - rip6stat.rip6s_fullsock; if (delivered || sflag <= 1) xo_emit("\t{:delivered-packets/%ju} {N:/delivered}\n", (uintmax_t)delivered); p(rip6s_opackets, "\t{:sent-packets/%ju} " "{N:/datagram%s output}\n"); #undef p xo_close_container(name); } /* * Pretty print an Internet address (net address + port). * Take numeric_addr and numeric_port into consideration. */ #define GETSERVBYPORT6(port, proto, ret)\ {\ if (strcmp((proto), "tcp6") == 0)\ (ret) = getservbyport((int)(port), "tcp");\ else if (strcmp((proto), "udp6") == 0)\ (ret) = getservbyport((int)(port), "udp");\ else\ (ret) = getservbyport((int)(port), (proto));\ }; void inet6print(const char *container, struct in6_addr *in6, int port, const char *proto, int numeric) { struct servent *sp = 0; char line[80], *cp; int width; size_t alen, plen; if (container) xo_open_container(container); snprintf(line, sizeof(line), "%.*s.", Wflag ? 39 : (Aflag && !numeric) ? 12 : 16, inet6name(in6)); alen = strlen(line); cp = line + alen; if (!numeric && port) GETSERVBYPORT6(port, proto, sp); if (sp || port == 0) snprintf(cp, sizeof(line) - alen, "%.15s", sp ? sp->s_name : "*"); else snprintf(cp, sizeof(line) - alen, "%d", ntohs((u_short)port)); width = Wflag ? 45 : Aflag ? 18 : 22; xo_emit("{d:target/%-*.*s} ", width, width, line); plen = strlen(cp); alen--; xo_emit("{e:address/%*.*s}{e:port/%*.*s}", alen, alen, line, plen, plen, cp); if (container) xo_close_container(container); } /* * Construct an Internet address representation. * If the numeric_addr has been supplied, give * numeric value, otherwise try for symbolic name. */ char * inet6name(struct in6_addr *ia6) { struct sockaddr_in6 sin6; char hbuf[NI_MAXHOST], *cp; static char line[NI_MAXHOST]; static char domain[MAXHOSTNAMELEN]; static int first = 1; int flags, error; if (IN6_IS_ADDR_UNSPECIFIED(ia6)) { strcpy(line, "*"); return (line); } if (first && !numeric_addr) { first = 0; if (gethostname(domain, sizeof(domain)) == 0 && (cp = strchr(domain, '.'))) strlcpy(domain, cp + 1, sizeof(domain)); else domain[0] = 0; } memset(&sin6, 0, sizeof(sin6)); memcpy(&sin6.sin6_addr, ia6, sizeof(*ia6)); sin6.sin6_family = AF_INET6; /* XXX: ia6.s6_addr[2] can contain scopeid. */ in6_fillscopeid(&sin6); flags = (numeric_addr) ? NI_NUMERICHOST : 0; error = getnameinfo((struct sockaddr *)&sin6, sizeof(sin6), hbuf, sizeof(hbuf), NULL, 0, flags); if (error == 0) { if ((flags & NI_NUMERICHOST) == 0 && (cp = strchr(hbuf, '.')) && !strcmp(cp + 1, domain)) *cp = 0; strlcpy(line, hbuf, sizeof(line)); } else { /* XXX: this should not happen. */ snprintf(line, sizeof(line), "%s", inet_ntop(AF_INET6, (void *)&sin6.sin6_addr, ntop_buf, sizeof(ntop_buf))); } return (line); } #endif /*INET6*/ diff --git a/usr.bin/netstat/ipsec.c b/usr.bin/netstat/ipsec.c index 64a948b2e216..cac42b81325f 100644 --- a/usr.bin/netstat/ipsec.c +++ b/usr.bin/netstat/ipsec.c @@ -1,437 +1,433 @@ /* $KAME: ipsec.c,v 1.33 2003/07/25 09:54:32 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2005 NTT Multimedia Communications Laboratories, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. * 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 project 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 PROJECT 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 PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #ifdef IPSEC #include #include #include #include #endif #include #include #include #include #include #include #include "netstat.h" #ifdef IPSEC struct val2str { int val; const char *str; }; static struct val2str ipsec_ahnames[] = { { SADB_AALG_NONE, "none", }, { SADB_AALG_SHA1HMAC, "hmac-sha1", }, { SADB_X_AALG_NULL, "null", }, { SADB_X_AALG_SHA2_256, "hmac-sha2-256", }, { SADB_X_AALG_SHA2_384, "hmac-sha2-384", }, { SADB_X_AALG_SHA2_512, "hmac-sha2-512", }, { SADB_X_AALG_AES_XCBC_MAC, "aes-xcbc-mac", }, { SADB_X_AALG_TCP_MD5, "tcp-md5", }, { SADB_X_AALG_AES128GMAC, "aes-gmac-128", }, { SADB_X_AALG_AES192GMAC, "aes-gmac-192", }, { SADB_X_AALG_AES256GMAC, "aes-gmac-256", }, { -1, NULL }, }; static struct val2str ipsec_espnames[] = { { SADB_EALG_NONE, "none", }, { SADB_EALG_NULL, "null", }, { SADB_X_EALG_AESCBC, "aes-cbc", }, { SADB_X_EALG_AESCTR, "aes-ctr", }, { SADB_X_EALG_AESGCM16, "aes-gcm-16", }, { SADB_X_EALG_AESGMAC, "aes-gmac", }, { -1, NULL }, }; static struct val2str ipsec_compnames[] = { { SADB_X_CALG_NONE, "none", }, { SADB_X_CALG_OUI, "oui", }, { SADB_X_CALG_DEFLATE, "deflate", }, { SADB_X_CALG_LZS, "lzs", }, { -1, NULL }, }; static void print_ipsecstats(const char *tag, const struct ipsecstat *ipsecstat) { xo_open_container(tag); #define p(f, m) if (ipsecstat->f || sflag <= 1) \ xo_emit(m, (uintmax_t)ipsecstat->f, plural(ipsecstat->f)) #define p2(f, m) if (ipsecstat->f || sflag <= 1) \ xo_emit(m, (uintmax_t)ipsecstat->f, plurales(ipsecstat->f)) p(ips_in_polvio, "\t{:dropped-policy-violation/%ju} " "{N:/inbound packet%s violated process security policy}\n"); p(ips_in_nomem, "\t{:dropped-no-memory/%ju} " "{N:/inbound packet%s failed due to insufficient memory}\n"); p(ips_in_inval, "\t{:dropped-invalid/%ju} " "{N:/invalid inbound packet%s}\n"); p(ips_out_polvio, "\t{:discarded-policy-violation/%ju} " "{N:/outbound packet%s violated process security policy}\n"); p(ips_out_nosa, "\t{:discarded-no-sa/%ju} " "{N:/outbound packet%s with no SA available}\n"); p(ips_out_nomem, "\t{:discarded-no-memory/%ju} " "{N:/outbound packet%s failed due to insufficient memory}\n"); p(ips_out_noroute, "\t{:discarded-no-route/%ju} " "{N:/outbound packet%s with no route available}\n"); p(ips_out_inval, "\t{:discarded-invalid/%ju} " "{N:/invalid outbound packet%s}\n"); p(ips_out_bundlesa, "\t{:send-bundled-sa/%ju} " "{N:/outbound packet%s with bundled SAs}\n"); p(ips_spdcache_hits, "\t{:spdcache-hits/%ju} " "{N:/spd cache hit%s}\n"); p2(ips_spdcache_misses, "\t{:spdcache-misses/%ju} " "{N:/spd cache miss%s}\n"); p(ips_clcopied, "\t{:clusters-copied-during-clone/%ju} " "{N:/cluster%s copied during clone}\n"); p(ips_mbinserted, "\t{:mbufs-inserted/%ju} " "{N:/mbuf%s inserted during makespace}\n"); #undef p2 #undef p xo_close_container(tag); } void ipsec_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct ipsecstat ipsecstat; const char *tag; if (strcmp(name, "ipsec6") == 0) { if (fetch_stats("net.inet6.ipsec6.ipsecstats", off,&ipsecstat, sizeof(ipsecstat), kread_counters) != 0) return; tag = "ipsec6-statistics"; } else { if (fetch_stats("net.inet.ipsec.ipsecstats", off, &ipsecstat, sizeof(ipsecstat), kread_counters) != 0) return; tag = "ipsec-statistics"; } xo_emit("{T:/%s}:\n", name); print_ipsecstats(tag, &ipsecstat); } static void print_ahstats(const struct ahstat *ahstat); static void print_espstats(const struct espstat *espstat); static void print_ipcompstats(const struct ipcompstat *ipcompstat); /* * Dump IPSEC statistics structure. */ static void ipsec_hist_new(const uint64_t *hist, size_t histmax, const struct val2str *name, const char *title, const char *cname) { int first; size_t proto; const struct val2str *p; first = 1; for (proto = 0; proto < histmax; proto++) { if (hist[proto] <= 0) continue; if (first) { xo_open_list(cname); xo_emit("\t{T:/%s histogram}:\n", title); first = 0; } xo_open_instance(cname); for (p = name; p && p->str; p++) { if (p->val == (int)proto) break; } if (p && p->str) { xo_emit("\t\t{k:name}: {:count/%ju}\n", p->str, (uintmax_t)hist[proto]); } else { xo_emit("\t\t#{k:name/%lu}: {:count/%ju}\n", (unsigned long)proto, (uintmax_t)hist[proto]); } xo_close_instance(cname); } if (!first) xo_close_list(cname); } static void print_ahstats(const struct ahstat *ahstat) { xo_open_container("ah-statictics"); #define p(f, n, m) if (ahstat->f || sflag <= 1) \ xo_emit("\t{:" n "/%ju} {N:/" m "}\n", \ (uintmax_t)ahstat->f, plural(ahstat->f)) #define hist(f, n, t, c) \ ipsec_hist_new((f), sizeof(f)/sizeof(f[0]), (n), (t), (c)) p(ahs_hdrops, "dropped-short-header", "packet%s shorter than header shows"); p(ahs_nopf, "dropped-bad-protocol", "packet%s dropped; protocol family not supported"); p(ahs_notdb, "dropped-no-tdb", "packet%s dropped; no TDB"); p(ahs_badkcr, "dropped-bad-kcr", "packet%s dropped; bad KCR"); p(ahs_qfull, "dropped-queue-full", "packet%s dropped; queue full"); p(ahs_noxform, "dropped-no-transform", "packet%s dropped; no transform"); p(ahs_wrap, "replay-counter-wraps", "replay counter wrap%s"); p(ahs_badauth, "dropped-bad-auth", "packet%s dropped; bad authentication detected"); p(ahs_badauthl, "dropped-bad-auth-level", "packet%s dropped; bad authentication length"); p(ahs_replay, "possile-replay-detected", "possible replay packet%s detected"); p(ahs_input, "received-packets", "packet%s in"); p(ahs_output, "send-packets", "packet%s out"); p(ahs_invalid, "dropped-bad-tdb", "packet%s dropped; invalid TDB"); p(ahs_ibytes, "received-bytes", "byte%s in"); p(ahs_obytes, "send-bytes", "byte%s out"); p(ahs_toobig, "dropped-too-large", "packet%s dropped; larger than IP_MAXPACKET"); p(ahs_pdrops, "dropped-policy-violation", "packet%s blocked due to policy"); p(ahs_crypto, "crypto-failures", "crypto processing failure%s"); p(ahs_tunnel, "tunnel-failures", "tunnel sanity check failure%s"); hist(ahstat->ahs_hist, ipsec_ahnames, "AH output", "ah-output-histogram"); #undef p #undef hist xo_close_container("ah-statictics"); } void ah_stats(u_long off, const char *name, int family __unused, int proto __unused) { struct ahstat ahstat; if (fetch_stats("net.inet.ah.stats", off, &ahstat, sizeof(ahstat), kread_counters) != 0) return; xo_emit("{T:/%s}:\n", name); print_ahstats(&ahstat); } static void print_espstats(const struct espstat *espstat) { xo_open_container("esp-statictics"); #define p(f, n, m) if (espstat->f || sflag <= 1) \ xo_emit("\t{:" n "/%ju} {N:/" m "}\n", \ (uintmax_t)espstat->f, plural(espstat->f)) #define hist(f, n, t, c) \ ipsec_hist_new((f), sizeof(f)/sizeof(f[0]), (n), (t), (c)); p(esps_hdrops, "dropped-short-header", "packet%s shorter than header shows"); p(esps_nopf, "dropped-bad-protocol", "packet%s dropped; protocol family not supported"); p(esps_notdb, "dropped-no-tdb", "packet%s dropped; no TDB"); p(esps_badkcr, "dropped-bad-kcr", "packet%s dropped; bad KCR"); p(esps_qfull, "dropped-queue-full", "packet%s dropped; queue full"); p(esps_noxform, "dropped-no-transform", "packet%s dropped; no transform"); p(esps_badilen, "dropped-bad-length", "packet%s dropped; bad ilen"); p(esps_wrap, "replay-counter-wraps", "replay counter wrap%s"); p(esps_badenc, "dropped-bad-crypto", "packet%s dropped; bad encryption detected"); p(esps_badauth, "dropped-bad-auth", "packet%s dropped; bad authentication detected"); p(esps_replay, "possible-replay-detected", "possible replay packet%s detected"); p(esps_input, "received-packets", "packet%s in"); p(esps_output, "sent-packets", "packet%s out"); p(esps_invalid, "dropped-bad-tdb", "packet%s dropped; invalid TDB"); p(esps_ibytes, "receive-bytes", "byte%s in"); p(esps_obytes, "sent-bytes", "byte%s out"); p(esps_toobig, "dropped-too-large", "packet%s dropped; larger than IP_MAXPACKET"); p(esps_pdrops, "dropped-policy-violation", "packet%s blocked due to policy"); p(esps_crypto, "crypto-failures", "crypto processing failure%s"); p(esps_tunnel, "tunnel-failures", "tunnel sanity check failure%s"); hist(espstat->esps_hist, ipsec_espnames, "ESP output", "esp-output-histogram"); #undef p #undef hist xo_close_container("esp-statictics"); } void esp_stats(u_long off, const char *name, int family __unused, int proto __unused) { struct espstat espstat; if (fetch_stats("net.inet.esp.stats", off, &espstat, sizeof(espstat), kread_counters) != 0) return; xo_emit("{T:/%s}:\n", name); print_espstats(&espstat); } static void print_ipcompstats(const struct ipcompstat *ipcompstat) { xo_open_container("ipcomp-statictics"); #define p(f, n, m) if (ipcompstat->f || sflag <= 1) \ xo_emit("\t{:" n "/%ju} {N:/" m "}\n", \ (uintmax_t)ipcompstat->f, plural(ipcompstat->f)) #define hist(f, n, t, c) \ ipsec_hist_new((f), sizeof(f)/sizeof(f[0]), (n), (t), (c)); p(ipcomps_hdrops, "dropped-short-header", "packet%s shorter than header shows"); p(ipcomps_nopf, "dropped-bad-protocol", "packet%s dropped; protocol family not supported"); p(ipcomps_notdb, "dropped-no-tdb", "packet%s dropped; no TDB"); p(ipcomps_badkcr, "dropped-bad-kcr", "packet%s dropped; bad KCR"); p(ipcomps_qfull, "dropped-queue-full", "packet%s dropped; queue full"); p(ipcomps_noxform, "dropped-no-transform", "packet%s dropped; no transform"); p(ipcomps_wrap, "replay-counter-wraps", "replay counter wrap%s"); p(ipcomps_input, "receive-packets", "packet%s in"); p(ipcomps_output, "sent-packets", "packet%s out"); p(ipcomps_invalid, "dropped-bad-tdb", "packet%s dropped; invalid TDB"); p(ipcomps_ibytes, "received-bytes", "byte%s in"); p(ipcomps_obytes, "sent-bytes", "byte%s out"); p(ipcomps_toobig, "dropped-too-large", "packet%s dropped; larger than IP_MAXPACKET"); p(ipcomps_pdrops, "dropped-policy-violation", "packet%s blocked due to policy"); p(ipcomps_crypto, "crypto-failure", "crypto processing failure%s"); hist(ipcompstat->ipcomps_hist, ipsec_compnames, "COMP output", "comp-output-histogram"); p(ipcomps_threshold, "sent-uncompressed-small-packets", "packet%s sent uncompressed; size < compr. algo. threshold"); p(ipcomps_uncompr, "sent-uncompressed-useless-packets", "packet%s sent uncompressed; compression was useless"); #undef p #undef hist xo_close_container("ipcomp-statictics"); } void ipcomp_stats(u_long off, const char *name, int family __unused, int proto __unused) { struct ipcompstat ipcompstat; if (fetch_stats("net.inet.ipcomp.stats", off, &ipcompstat, sizeof(ipcompstat), kread_counters) != 0) return; xo_emit("{T:/%s}:\n", name); print_ipcompstats(&ipcompstat); } #endif /*IPSEC*/ diff --git a/usr.bin/netstat/mbuf.c b/usr.bin/netstat/mbuf.c index 87b9245dafeb..ed700720ecb5 100644 --- a/usr.bin/netstat/mbuf.c +++ b/usr.bin/netstat/mbuf.c @@ -1,359 +1,355 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. * Copyright (c) 2005 Robert N. M. Watson * 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 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" /* * Print mbuf statistics. */ void mbpr(void *kvmd, u_long mbaddr) { struct memory_type_list *mtlp; struct memory_type *mtp; uintmax_t mbuf_count, mbuf_bytes, mbuf_free, mbuf_failures, mbuf_size; uintmax_t mbuf_sleeps; uintmax_t cluster_count, cluster_limit, cluster_free; uintmax_t cluster_failures, cluster_size, cluster_sleeps; uintmax_t packet_count, packet_bytes, packet_free, packet_failures; uintmax_t packet_sleeps; uintmax_t tag_bytes; uintmax_t jumbop_count, jumbop_limit, jumbop_free; uintmax_t jumbop_failures, jumbop_sleeps, jumbop_size; uintmax_t jumbo9_count, jumbo9_limit, jumbo9_free; uintmax_t jumbo9_failures, jumbo9_sleeps, jumbo9_size; uintmax_t jumbo16_count, jumbo16_limit, jumbo16_free; uintmax_t jumbo16_failures, jumbo16_sleeps, jumbo16_size; uintmax_t bytes_inuse, bytes_incache, bytes_total; int nsfbufs, nsfbufspeak, nsfbufsused; struct sfstat sfstat; size_t mlen; int error; mtlp = memstat_mtl_alloc(); if (mtlp == NULL) { xo_warn("memstat_mtl_alloc"); return; } /* * Use memstat_*_all() because some mbuf-related memory is in uma(9), * and some malloc(9). */ if (live) { if (memstat_sysctl_all(mtlp, 0) < 0) { xo_warnx("memstat_sysctl_all: %s", memstat_strerror(memstat_mtl_geterror(mtlp))); goto out; } } else { if (memstat_kvm_all(mtlp, kvmd) < 0) { error = memstat_mtl_geterror(mtlp); if (error == MEMSTAT_ERROR_KVM) xo_warnx("memstat_kvm_all: %s", kvm_geterr(kvmd)); else xo_warnx("memstat_kvm_all: %s", memstat_strerror(error)); goto out; } } mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_MEM_NAME); if (mtp == NULL) { xo_warnx("memstat_mtl_find: zone %s not found", MBUF_MEM_NAME); goto out; } mbuf_count = memstat_get_count(mtp); mbuf_bytes = memstat_get_bytes(mtp); mbuf_free = memstat_get_free(mtp); mbuf_failures = memstat_get_failures(mtp); mbuf_sleeps = memstat_get_sleeps(mtp); mbuf_size = memstat_get_size(mtp); mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_PACKET_MEM_NAME); if (mtp == NULL) { xo_warnx("memstat_mtl_find: zone %s not found", MBUF_PACKET_MEM_NAME); goto out; } packet_count = memstat_get_count(mtp); packet_bytes = memstat_get_bytes(mtp); packet_free = memstat_get_free(mtp); packet_sleeps = memstat_get_sleeps(mtp); packet_failures = memstat_get_failures(mtp); mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_CLUSTER_MEM_NAME); if (mtp == NULL) { xo_warnx("memstat_mtl_find: zone %s not found", MBUF_CLUSTER_MEM_NAME); goto out; } cluster_count = memstat_get_count(mtp); cluster_limit = memstat_get_countlimit(mtp); cluster_free = memstat_get_free(mtp); cluster_failures = memstat_get_failures(mtp); cluster_sleeps = memstat_get_sleeps(mtp); cluster_size = memstat_get_size(mtp); mtp = memstat_mtl_find(mtlp, ALLOCATOR_MALLOC, MBUF_TAG_MEM_NAME); if (mtp == NULL) { xo_warnx("memstat_mtl_find: malloc type %s not found", MBUF_TAG_MEM_NAME); goto out; } tag_bytes = memstat_get_bytes(mtp); mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_JUMBOP_MEM_NAME); if (mtp == NULL) { xo_warnx("memstat_mtl_find: zone %s not found", MBUF_JUMBOP_MEM_NAME); goto out; } jumbop_count = memstat_get_count(mtp); jumbop_limit = memstat_get_countlimit(mtp); jumbop_free = memstat_get_free(mtp); jumbop_failures = memstat_get_failures(mtp); jumbop_sleeps = memstat_get_sleeps(mtp); jumbop_size = memstat_get_size(mtp); mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_JUMBO9_MEM_NAME); if (mtp == NULL) { xo_warnx("memstat_mtl_find: zone %s not found", MBUF_JUMBO9_MEM_NAME); goto out; } jumbo9_count = memstat_get_count(mtp); jumbo9_limit = memstat_get_countlimit(mtp); jumbo9_free = memstat_get_free(mtp); jumbo9_failures = memstat_get_failures(mtp); jumbo9_sleeps = memstat_get_sleeps(mtp); jumbo9_size = memstat_get_size(mtp); mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, MBUF_JUMBO16_MEM_NAME); if (mtp == NULL) { xo_warnx("memstat_mtl_find: zone %s not found", MBUF_JUMBO16_MEM_NAME); goto out; } jumbo16_count = memstat_get_count(mtp); jumbo16_limit = memstat_get_countlimit(mtp); jumbo16_free = memstat_get_free(mtp); jumbo16_failures = memstat_get_failures(mtp); jumbo16_sleeps = memstat_get_sleeps(mtp); jumbo16_size = memstat_get_size(mtp); xo_open_container("mbuf-statistics"); xo_emit("{:mbuf-current/%ju}/{:mbuf-cache/%ju}/{:mbuf-total/%ju} " "{N:mbufs in use (current\\/cache\\/total)}\n", mbuf_count + packet_count, mbuf_free + packet_free, mbuf_count + packet_count + mbuf_free + packet_free); xo_emit("{:cluster-current/%ju}/{:cluster-cache/%ju}/" "{:cluster-total/%ju}/{:cluster-max/%ju} " "{N:mbuf clusters in use (current\\/cache\\/total\\/max)}\n", cluster_count - packet_free, cluster_free + packet_free, cluster_count + cluster_free, cluster_limit); xo_emit("{:packet-count/%ju}/{:packet-free/%ju} " "{N:mbuf+clusters out of packet secondary zone in use " "(current\\/cache)}\n", packet_count, packet_free); xo_emit("{:jumbo-count/%ju}/{:jumbo-cache/%ju}/{:jumbo-total/%ju}/" "{:jumbo-max/%ju} {:jumbo-page-size/%ju}{U:k} {N:(page size)} " "{N:jumbo clusters in use (current\\/cache\\/total\\/max)}\n", jumbop_count, jumbop_free, jumbop_count + jumbop_free, jumbop_limit, jumbop_size / 1024); xo_emit("{:jumbo9-count/%ju}/{:jumbo9-cache/%ju}/" "{:jumbo9-total/%ju}/{:jumbo9-max/%ju} " "{N:9k jumbo clusters in use (current\\/cache\\/total\\/max)}\n", jumbo9_count, jumbo9_free, jumbo9_count + jumbo9_free, jumbo9_limit); xo_emit("{:jumbo16-count/%ju}/{:jumbo16-cache/%ju}/" "{:jumbo16-total/%ju}/{:jumbo16-limit/%ju} " "{N:16k jumbo clusters in use (current\\/cache\\/total\\/max)}\n", jumbo16_count, jumbo16_free, jumbo16_count + jumbo16_free, jumbo16_limit); #if 0 xo_emit("{:tag-count/%ju} {N:mbuf tags in use}\n", tag_count); #endif /*- * Calculate in-use bytes as: * - straight mbuf memory * - mbuf memory in packets * - the clusters attached to packets * - and the rest of the non-packet-attached clusters. * - m_tag memory * This avoids counting the clusters attached to packets in the cache. * This currently excludes sf_buf space. */ bytes_inuse = mbuf_bytes + /* straight mbuf memory */ packet_bytes + /* mbufs in packets */ (packet_count * cluster_size) + /* clusters in packets */ /* other clusters */ ((cluster_count - packet_count - packet_free) * cluster_size) + tag_bytes + (jumbop_count * jumbop_size) + /* jumbo clusters */ (jumbo9_count * jumbo9_size) + (jumbo16_count * jumbo16_size); /* * Calculate in-cache bytes as: * - cached straught mbufs * - cached packet mbufs * - cached packet clusters * - cached straight clusters * This currently excludes sf_buf space. */ bytes_incache = (mbuf_free * mbuf_size) + /* straight free mbufs */ (packet_free * mbuf_size) + /* mbufs in free packets */ (packet_free * cluster_size) + /* clusters in free packets */ (cluster_free * cluster_size) + /* free clusters */ (jumbop_free * jumbop_size) + /* jumbo clusters */ (jumbo9_free * jumbo9_size) + (jumbo16_free * jumbo16_size); /* * Total is bytes in use + bytes in cache. This doesn't take into * account various other misc data structures, overhead, etc, but * gives the user something useful despite that. */ bytes_total = bytes_inuse + bytes_incache; xo_emit("{:bytes-in-use/%ju}{U:K}/{:bytes-in-cache/%ju}{U:K}/" "{:bytes-total/%ju}{U:K} " "{N:bytes allocated to network (current\\/cache\\/total)}\n", bytes_inuse / 1024, bytes_incache / 1024, bytes_total / 1024); xo_emit("{:mbuf-failures/%ju}/{:cluster-failures/%ju}/" "{:packet-failures/%ju} {N:requests for mbufs denied " "(mbufs\\/clusters\\/mbuf+clusters)}\n", mbuf_failures, cluster_failures, packet_failures); xo_emit("{:mbuf-sleeps/%ju}/{:cluster-sleeps/%ju}/{:packet-sleeps/%ju} " "{N:requests for mbufs delayed " "(mbufs\\/clusters\\/mbuf+clusters)}\n", mbuf_sleeps, cluster_sleeps, packet_sleeps); xo_emit("{:jumbop-sleeps/%ju}/{:jumbo9-sleeps/%ju}/" "{:jumbo16-sleeps/%ju} {N:/requests for jumbo clusters delayed " "(%juk\\/9k\\/16k)}\n", jumbop_sleeps, jumbo9_sleeps, jumbo16_sleeps, jumbop_size / 1024); xo_emit("{:jumbop-failures/%ju}/{:jumbo9-failures/%ju}/" "{:jumbo16-failures/%ju} {N:/requests for jumbo clusters denied " "(%juk\\/9k\\/16k)}\n", jumbop_failures, jumbo9_failures, jumbo16_failures, jumbop_size / 1024); mlen = sizeof(nsfbufs); if (live && sysctlbyname("kern.ipc.nsfbufs", &nsfbufs, &mlen, NULL, 0) == 0 && sysctlbyname("kern.ipc.nsfbufsused", &nsfbufsused, &mlen, NULL, 0) == 0 && sysctlbyname("kern.ipc.nsfbufspeak", &nsfbufspeak, &mlen, NULL, 0) == 0) xo_emit("{:nsfbufs-current/%d}/{:nsfbufs-peak/%d}/" "{:nsfbufs/%d} " "{N:sfbufs in use (current\\/peak\\/max)}\n", nsfbufsused, nsfbufspeak, nsfbufs); if (fetch_stats("kern.ipc.sfstat", mbaddr, &sfstat, sizeof(sfstat), kread_counters) != 0) goto out; xo_emit("{:sendfile-syscalls/%ju} {N:sendfile syscalls}\n", (uintmax_t)sfstat.sf_syscalls); xo_emit("{:sendfile-no-io/%ju} " "{N:sendfile syscalls completed without I\\/O request}\n", (uintmax_t)sfstat.sf_noiocnt); xo_emit("{:sendfile-io-count/%ju} " "{N:requests for I\\/O initiated by sendfile}\n", (uintmax_t)sfstat.sf_iocnt); xo_emit("{:sendfile-pages-sent/%ju} " "{N:pages read by sendfile as part of a request}\n", (uintmax_t)sfstat.sf_pages_read); xo_emit("{:sendfile-pages-valid/%ju} " "{N:pages were valid at time of a sendfile request}\n", (uintmax_t)sfstat.sf_pages_valid); xo_emit("{:sendfile-pages-bogus/%ju} " "{N:pages were valid and substituted to bogus page}\n", (uintmax_t)sfstat.sf_pages_bogus); xo_emit("{:sendfile-requested-readahead/%ju} " "{N:pages were requested for read ahead by applications}\n", (uintmax_t)sfstat.sf_rhpages_requested); xo_emit("{:sendfile-readahead/%ju} " "{N:pages were read ahead by sendfile}\n", (uintmax_t)sfstat.sf_rhpages_read); xo_emit("{:sendfile-busy-encounters/%ju} " "{N:times sendfile encountered an already busy page}\n", (uintmax_t)sfstat.sf_busy); xo_emit("{:sfbufs-alloc-failed/%ju} {N:requests for sfbufs denied}\n", (uintmax_t)sfstat.sf_allocfail); xo_emit("{:sfbufs-alloc-wait/%ju} {N:requests for sfbufs delayed}\n", (uintmax_t)sfstat.sf_allocwait); out: xo_close_container("mbuf-statistics"); memstat_mtl_free(mtlp); } diff --git a/usr.bin/netstat/netgraph.c b/usr.bin/netstat/netgraph.c index 95821efc39ac..4511b17ddbbe 100644 --- a/usr.bin/netstat/netgraph.c +++ b/usr.bin/netstat/netgraph.c @@ -1,143 +1,142 @@ /*- * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ -#include #include #include #include #define _WANT_SOCKET #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" static int first = 1; static int csock = -1; void netgraphprotopr(u_long off, const char *name, int af1 __unused, int proto __unused) { struct ngpcb *this, *next; struct ngpcb ngpcb; struct socket sockb; int debug = 1; /* If symbol not found, try looking in the KLD module */ if (off == 0) { if (debug) xo_warnx("Error reading symbols from ng_socket.ko"); return; } /* Get pointer to first socket */ kread(off, (char *)&this, sizeof(this)); /* Get my own socket node */ if (csock == -1) NgMkSockNode(NULL, &csock, NULL); for (; this != NULL; this = next) { u_char rbuf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)]; struct ng_mesg *resp = (struct ng_mesg *) rbuf; struct nodeinfo *ni = (struct nodeinfo *) resp->data; char path[64]; /* Read in ngpcb structure */ kread((u_long)this, (char *)&ngpcb, sizeof(ngpcb)); next = LIST_NEXT(&ngpcb, socks); /* Read in socket structure */ kread((u_long)ngpcb.ng_socket, (char *)&sockb, sizeof(sockb)); /* Check type of socket */ if (strcmp(name, "ctrl") == 0 && ngpcb.type != NG_CONTROL) continue; if (strcmp(name, "data") == 0 && ngpcb.type != NG_DATA) continue; /* Do headline */ if (first) { xo_emit("{T:Netgraph sockets}\n"); if (Aflag) xo_emit("{T:/%-8.8s} ", "PCB"); xo_emit("{T:/%-5.5s} {T:/%-6.6s} {T:/%-6.6s} " "{T:/%-14.14s} {T:/%s}\n", "Type", "Recv-Q", "Send-Q", "Node Address", "#Hooks"); first = 0; } /* Show socket */ if (Aflag) xo_emit("{:address/%8lx} ", (u_long) this); xo_emit("{t:name/%-5.5s} {:receive-bytes-waiting/%6u} " "{:send-byte-waiting/%6u} ", name, sockb.so_rcv.sb_ccc, sockb.so_snd.sb_ccc); /* Get info on associated node */ if (ngpcb.node_id == 0 || csock == -1) goto finish; snprintf(path, sizeof(path), "[%x]:", ngpcb.node_id); if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) < 0) goto finish; if (NgRecvMsg(csock, resp, sizeof(rbuf), NULL) < 0) goto finish; /* Display associated node info */ if (*ni->name != '\0') snprintf(path, sizeof(path), "%s:", ni->name); xo_emit("{t:path/%-14.14s} {:hooks/%4d}", path, ni->hooks); finish: xo_emit("\n"); } } diff --git a/usr.bin/netstat/nhgrp.c b/usr.bin/netstat/nhgrp.c index 99bc5ce7d593..00b051a04d21 100644 --- a/usr.bin/netstat/nhgrp.c +++ b/usr.bin/netstat/nhgrp.c @@ -1,354 +1,353 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020 Alexander V. Chernikov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" #include "common.h" #define WID_GW_DEFAULT(af) (((af) == AF_INET6) ? 40 : 18) static int wid_gw; static int wid_if = 10; static int wid_nhidx = 8; static int wid_refcnt = 8; struct nhop_entry { char gw[64]; char ifname[IFNAMSIZ]; }; struct nhop_map { struct nhop_entry *ptr; size_t size; }; static struct nhop_map global_nhop_map; static struct ifmap_entry *ifmap; static size_t ifmap_size; static struct nhop_entry * nhop_get(struct nhop_map *map, uint32_t idx) { if (idx >= map->size) return (NULL); if (*map->ptr[idx].ifname == '\0') return (NULL); return &map->ptr[idx]; } static void print_nhgroup_header(int af1 __unused) { xo_emit("{T:/%-*.*s}{T:/%-*.*s}{T:/%*.*s}{T:/%*.*s}{T:/%*.*s}" "{T:/%*.*s}{T:/%*s}\n", wid_nhidx, wid_nhidx, "GrpIdx", wid_nhidx, wid_nhidx, "NhIdx", wid_nhidx, wid_nhidx, "Weight", wid_nhidx, wid_nhidx, "Slots", wid_gw, wid_gw, "Gateway", wid_if, wid_if, "Netif", wid_refcnt, "Refcnt"); } static void print_padding(char sym, int len) { char buffer[56]; memset(buffer, sym, sizeof(buffer)); buffer[0] = '{'; buffer[1] = 'P'; buffer[2] = ':'; buffer[3] = ' '; buffer[len + 3] = '}'; buffer[len + 4] = '\0'; xo_emit(buffer); } static void print_nhgroup_entry_sysctl(const char *name, struct rt_msghdr *rtm, struct nhgrp_external *nhge) { char buffer[128]; struct nhop_entry *ne; struct nhgrp_nhop_external *ext_cp, *ext_dp; struct nhgrp_container *nhg_cp, *nhg_dp; nhg_cp = (struct nhgrp_container *)(nhge + 1); if (nhg_cp->nhgc_type != NHG_C_TYPE_CNHOPS || nhg_cp->nhgc_subtype != 0) return; ext_cp = (struct nhgrp_nhop_external *)(nhg_cp + 1); nhg_dp = (struct nhgrp_container *)((char *)nhg_cp + nhg_cp->nhgc_len); if (nhg_dp->nhgc_type != NHG_C_TYPE_DNHOPS || nhg_dp->nhgc_subtype != 0) return; ext_dp = (struct nhgrp_nhop_external *)(nhg_dp + 1); xo_open_instance(name); snprintf(buffer, sizeof(buffer), "{[:-%d}{:nhgrp-index/%%lu}{]:} ", wid_nhidx); xo_emit(buffer, nhge->nhg_idx); /* nhidx */ print_padding('-', wid_nhidx); /* weight */ print_padding('-', wid_nhidx); /* slots */ print_padding('-', wid_nhidx); print_padding('-', wid_gw); print_padding('-', wid_if); xo_emit("{t:nhg-refcnt/%*lu}", wid_refcnt, nhge->nhg_refcount); xo_emit("\n"); xo_open_list("nhop-weights"); for (uint32_t i = 0; i < nhg_cp->nhgc_count; i++) { /* TODO: optimize slots calculations */ uint32_t slots = 0; for (uint32_t sidx = 0; sidx < nhg_dp->nhgc_count; sidx++) { if (ext_dp[sidx].nh_idx == ext_cp[i].nh_idx) slots++; } xo_open_instance("nhop-weight"); print_padding(' ', wid_nhidx); // nh index xo_emit("{t:nh-index/%*lu}", wid_nhidx, ext_cp[i].nh_idx); xo_emit("{t:nh-weight/%*lu}", wid_nhidx, ext_cp[i].nh_weight); xo_emit("{t:nh-slots/%*lu}", wid_nhidx, slots); ne = nhop_get(&global_nhop_map, ext_cp[i].nh_idx); if (ne != NULL) { xo_emit("{t:nh-gw/%*.*s}", wid_gw, wid_gw, ne->gw); xo_emit("{t:nh-interface/%*.*s}", wid_if, wid_if, ne->ifname); } xo_emit("\n"); xo_close_instance("nhop-weight"); } xo_close_list("nhop-weights"); xo_close_instance(name); } static int cmp_nhg_idx(const void *_a, const void *_b) { const struct nhops_map *a, *b; a = _a; b = _b; if (a->idx > b->idx) return (1); else if (a->idx < b->idx) return (-1); return (0); } static void dump_nhgrp_sysctl(int fibnum, int af, struct nhops_dump *nd) { size_t needed; int mib[7]; char *buf, *next, *lim; struct rt_msghdr *rtm; struct nhgrp_external *nhg; struct nhops_map *nhg_map; size_t nhg_count, nhg_size; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = af; mib[4] = NET_RT_NHGRP; mib[5] = 0; mib[6] = fibnum; if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0) err(EX_OSERR, "sysctl: net.route.0.%d.nhgrpdump.%d estimate", af, fibnum); if ((buf = malloc(needed)) == NULL) errx(2, "malloc(%lu)", (unsigned long)needed); if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) err(1, "sysctl: net.route.0.%d.nhgrpdump.%d", af, fibnum); lim = buf + needed; /* * nexhops groups are received unsorted. Collect everything first, * and sort prior displaying. */ nhg_count = 0; nhg_size = 16; nhg_map = calloc(nhg_size, sizeof(struct nhops_map)); for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; if (rtm->rtm_version != RTM_VERSION) continue; if (nhg_count >= nhg_size) { nhg_size *= 2; nhg_map = realloc(nhg_map, nhg_size * sizeof(struct nhops_map)); } nhg = (struct nhgrp_external *)(rtm + 1); nhg_map[nhg_count].idx = nhg->nhg_idx; nhg_map[nhg_count].rtm = rtm; nhg_count++; } if (nhg_count > 0) qsort(nhg_map, nhg_count, sizeof(struct nhops_map), cmp_nhg_idx); nd->nh_buf = buf; nd->nh_count = nhg_count; nd->nh_map = nhg_map; } static void print_nhgrp_sysctl(int fibnum, int af) { struct nhops_dump nd; struct nhgrp_external *nhg; struct rt_msghdr *rtm; dump_nhgrp_sysctl(fibnum, af, &nd); xo_open_container("nhgrp-table"); xo_open_list("rt-family"); if (nd.nh_count > 0) { wid_gw = WID_GW_DEFAULT(af); xo_open_instance("rt-family"); pr_family(af); xo_open_list("nhgrp-entry"); print_nhgroup_header(af); for (size_t i = 0; i < nd.nh_count; i++) { rtm = nd.nh_map[i].rtm; nhg = (struct nhgrp_external *)(rtm + 1); print_nhgroup_entry_sysctl("nhgrp-entry", rtm, nhg); } } xo_close_list("rt-family"); xo_close_container("nhgrp-table"); free(nd.nh_buf); } static void update_global_map(struct nhop_external *nh) { char iface_name[128]; char gw_addr[64]; struct nhop_addrs *na; struct sockaddr *sa_gw; na = (struct nhop_addrs *)((char *)nh + nh->nh_len); sa_gw = (struct sockaddr *)((char *)na + na->gw_sa_off); memset(iface_name, 0, sizeof(iface_name)); if (nh->ifindex < (uint32_t)ifmap_size) { strlcpy(iface_name, ifmap[nh->ifindex].ifname, sizeof(iface_name)); if (*iface_name == '\0') strlcpy(iface_name, "---", sizeof(iface_name)); } if (nh->nh_flags & NHF_GATEWAY) { const char *cp; cp = fmt_sockaddr(sa_gw, NULL, RTF_HOST); strlcpy(gw_addr, cp, sizeof(gw_addr)); } else snprintf(gw_addr, sizeof(gw_addr), "%s/resolve", iface_name); nhop_map_update(&global_nhop_map, nh->nh_idx, gw_addr, iface_name); } static void prepare_nh_map(int fibnum, int af) { struct nhops_dump nd; struct nhop_external *nh; struct rt_msghdr *rtm; dump_nhops_sysctl(fibnum, af, &nd); for (size_t i = 0; i < nd.nh_count; i++) { rtm = nd.nh_map[i].rtm; nh = (struct nhop_external *)(rtm + 1); update_global_map(nh); } free(nd.nh_buf); } void nhgrp_print(int fibnum, int af) { size_t intsize; int numfibs; intsize = sizeof(int); if (fibnum == -1 && sysctlbyname("net.my_fibnum", &fibnum, &intsize, NULL, 0) == -1) fibnum = 0; if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1) numfibs = 1; if (fibnum < 0 || fibnum > numfibs - 1) errx(EX_USAGE, "%d: invalid fib", fibnum); ifmap = prepare_ifmap(&ifmap_size); prepare_nh_map(fibnum, af); xo_open_container("route-nhgrp-information"); xo_emit("{T:Nexthop groups data}"); if (fibnum) xo_emit(" ({L:fib}: {:fib/%d})", fibnum); xo_emit("\n"); print_nhgrp_sysctl(fibnum, af); xo_close_container("route-nhgrp-information"); } diff --git a/usr.bin/netstat/nhops.c b/usr.bin/netstat/nhops.c index 19a7a6891050..f4c76f54a420 100644 --- a/usr.bin/netstat/nhops.c +++ b/usr.bin/netstat/nhops.c @@ -1,480 +1,479 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" #include "common.h" /* column widths; each followed by one space */ #ifndef INET6 #define WID_DST_DEFAULT(af) 18 /* width of destination column */ #define WID_GW_DEFAULT(af) 18 /* width of gateway column */ #define WID_IF_DEFAULT(af) (Wflag ? 10 : 8) /* width of netif column */ #else #define WID_DST_DEFAULT(af) \ ((af) == AF_INET6 ? (numeric_addr ? 33: 18) : 18) #define WID_GW_DEFAULT(af) \ ((af) == AF_INET6 ? (numeric_addr ? 29 : 18) : 18) #define WID_IF_DEFAULT(af) ((af) == AF_INET6 ? 8 : (Wflag ? 10 : 8)) #endif /*INET6*/ static int wid_dst; static int wid_gw; static int wid_flags; static int wid_pksent; static int wid_mtu; static int wid_if; static int wid_nhidx; static int wid_nhtype; static int wid_refcnt; static int wid_prepend; static struct bits nh_bits[] = { { NHF_REJECT, 'R', "reject" }, { NHF_BLACKHOLE,'B', "blackhole" }, { NHF_REDIRECT, 'r', "redirect" }, { NHF_GATEWAY, 'G', "gateway" }, { NHF_DEFAULT, 'd', "default" }, { NHF_BROADCAST,'b', "broadcast" }, { 0 , 0, NULL } }; static char *nh_types[] = { "empty", /* 0 */ "v4/resolve", /* 1 */ "v4/gw", "v6/resolve", "v6/gw" }; struct nhop_entry { char gw[64]; char ifname[IFNAMSIZ]; }; struct nhop_map { struct nhop_entry *ptr; size_t size; }; static struct nhop_map global_nhop_map; static struct nhop_entry *nhop_get(struct nhop_map *map, uint32_t idx); static struct ifmap_entry *ifmap; static size_t ifmap_size; static void print_sockaddr_buf(char *buf, size_t bufsize, const struct sockaddr *sa) { switch (sa->sa_family) { case AF_INET: inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr, buf, bufsize); break; case AF_INET6: inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sa)->sin6_addr, buf, bufsize); break; default: snprintf(buf, bufsize, "unknown:%d", sa->sa_family); break; } } static int print_addr(const char *name, const char *addr, int width) { char buf[128]; int protrusion; if (width < 0) { snprintf(buf, sizeof(buf), "{:%s/%%s} ", name); xo_emit(buf, addr); protrusion = 0; } else { if (Wflag != 0 || numeric_addr) { snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%s}{]:} ", -width, name); xo_emit(buf, addr); protrusion = strlen(addr) - width; if (protrusion < 0) protrusion = 0; } else { snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%-.*s}{]:} ", -width, name); xo_emit(buf, width, addr); protrusion = 0; } } return (protrusion); } static void print_nhop_header(int af1 __unused) { if (Wflag) { xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} " "{T:/%*.*s} {T:/%-*.*s} {T:/%*.*s} {T:/%*.*s} {T:/%*.*s} {T:/%*s}\n", wid_nhidx, wid_nhidx, "Idx", wid_nhtype, wid_nhtype, "Type", wid_dst, wid_dst, "IFA", wid_gw, wid_gw, "Gateway", wid_flags, wid_flags, "Flags", wid_pksent, wid_pksent, "Use", wid_mtu, wid_mtu, "Mtu", wid_if, wid_if, "Netif", wid_if, wid_if, "Addrif", wid_refcnt, wid_refcnt, "Refcnt", wid_prepend, "Prepend"); } else { xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} " " {T:/%*s}\n", wid_nhidx, wid_nhidx, "Idx", wid_dst, wid_dst, "IFA", wid_gw, wid_gw, "Gateway", wid_flags, wid_flags, "Flags", wid_if, wid_if, "Netif", wid_prepend, "Refcnt"); } } void nhop_map_update(struct nhop_map *map, uint32_t idx, char *gw, char *ifname) { if (idx >= map->size) { uint32_t new_size; size_t sz; if (map->size == 0) new_size = 32; else new_size = map->size * 2; if (new_size <= idx) new_size = roundup2(idx + 1, 32); sz = new_size * (sizeof(struct nhop_entry)); if ((map->ptr = realloc(map->ptr, sz)) == NULL) errx(2, "realloc(%zu) failed", sz); memset(&map->ptr[map->size], 0, (new_size - map->size) * sizeof(struct nhop_entry)); map->size = new_size; } strlcpy(map->ptr[idx].ifname, ifname, sizeof(map->ptr[idx].ifname)); strlcpy(map->ptr[idx].gw, gw, sizeof(map->ptr[idx].gw)); } static struct nhop_entry * nhop_get(struct nhop_map *map, uint32_t idx) { if (idx >= map->size) return (NULL); if (*map->ptr[idx].ifname == '\0') return (NULL); return &map->ptr[idx]; } static void print_nhop_entry_sysctl(const char *name, struct rt_msghdr *rtm, struct nhop_external *nh) { char buffer[128]; char iface_name[128]; int protrusion; char gw_addr[64]; struct nhop_addrs *na; struct sockaddr *sa_gw, *sa_ifa; xo_open_instance(name); snprintf(buffer, sizeof(buffer), "{[:-%d}{:index/%%lu}{]:} ", wid_nhidx); //xo_emit("{t:index/%-lu} ", wid_nhidx, nh->nh_idx); xo_emit(buffer, nh->nh_idx); if (Wflag) { char *cp = nh_types[nh->nh_type]; xo_emit("{t:type_str/%*s} ", wid_nhtype, cp); } memset(iface_name, 0, sizeof(iface_name)); if (nh->ifindex < (uint32_t)ifmap_size) { strlcpy(iface_name, ifmap[nh->ifindex].ifname, sizeof(iface_name)); if (*iface_name == '\0') strlcpy(iface_name, "---", sizeof(iface_name)); } na = (struct nhop_addrs *)((char *)nh + nh->nh_len); //inet_ntop(nh->nh_family, &nh->nh_src, src_addr, sizeof(src_addr)); //protrusion = p_addr("ifa", src_addr, wid_dst); sa_gw = (struct sockaddr *)((char *)na + na->gw_sa_off); sa_ifa = (struct sockaddr *)((char *)na + na->src_sa_off); protrusion = p_sockaddr("ifa", sa_ifa, NULL, RTF_HOST, wid_dst); if (nh->nh_flags & NHF_GATEWAY) { const char *cp; cp = fmt_sockaddr(sa_gw, NULL, RTF_HOST); strlcpy(gw_addr, cp, sizeof(gw_addr)); } else snprintf(gw_addr, sizeof(gw_addr), "%s/resolve", iface_name); protrusion = print_addr("gateway", gw_addr, wid_dst - protrusion); nhop_map_update(&global_nhop_map, nh->nh_idx, gw_addr, iface_name); snprintf(buffer, sizeof(buffer), "{[:-%d}{:flags/%%s}{]:} ", wid_flags - protrusion); //p_nhflags(nh->nh_flags, buffer); print_flags_generic(rtm->rtm_flags, rt_bits, buffer, "rt_flags_pretty"); if (Wflag) { xo_emit("{t:use/%*lu} ", wid_pksent, nh->nh_pksent); xo_emit("{t:mtu/%*lu} ", wid_mtu, nh->nh_mtu); } //printf("IDX: %d IFACE: %s FAMILY: %d TYPE: %d FLAGS: %X GW \n"); if (Wflag) xo_emit("{t:interface-name/%*s}", wid_if, iface_name); else xo_emit("{t:interface-name/%*.*s}", wid_if, wid_if, iface_name); memset(iface_name, 0, sizeof(iface_name)); if (nh->aifindex < (uint32_t)ifmap_size && nh->ifindex != nh->aifindex) { strlcpy(iface_name, ifmap[nh->aifindex].ifname, sizeof(iface_name)); if (*iface_name == '\0') strlcpy(iface_name, "---", sizeof(iface_name)); } if (Wflag) xo_emit("{t:address-interface-name/%*s}", wid_if, iface_name); xo_emit("{t:refcount/%*lu} ", wid_refcnt, nh->nh_refcount); if (Wflag && nh->prepend_len) { int max_bytes = MIN(nh->prepend_len, sizeof(buffer) / 2 - 1); for (int i = 0; i < max_bytes; i++) snprintf(&buffer[i * 2], 3, "%02X", nh->nh_prepend[i]); xo_emit(" {:nhop-prepend/%*s}", wid_prepend, buffer); } xo_emit("\n"); xo_close_instance(name); } static int cmp_nh_idx(const void *_a, const void *_b) { const struct nhops_map *a, *b; a = _a; b = _b; if (a->idx > b->idx) return (1); else if (a->idx < b->idx) return (-1); return (0); } void dump_nhops_sysctl(int fibnum, int af, struct nhops_dump *nd) { size_t needed; int mib[7]; char *buf, *next, *lim; struct rt_msghdr *rtm; struct nhop_external *nh; struct nhops_map *nh_map; size_t nh_count, nh_size; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = af; mib[4] = NET_RT_NHOP; mib[5] = 0; mib[6] = fibnum; if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0) err(EX_OSERR, "sysctl: net.route.0.%d.nhdump.%d estimate", af, fibnum); if ((buf = malloc(needed)) == NULL) errx(2, "malloc(%lu)", (unsigned long)needed); if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) err(1, "sysctl: net.route.0.%d.nhdump.%d", af, fibnum); lim = buf + needed; /* * nexhops are received unsorted. Collect everything first, sort and then display * sorted. */ nh_count = 0; nh_size = 16; nh_map = calloc(nh_size, sizeof(struct nhops_map)); for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; if (rtm->rtm_version != RTM_VERSION) continue; if (nh_count >= nh_size) { nh_size *= 2; nh_map = realloc(nh_map, nh_size * sizeof(struct nhops_map)); } nh = (struct nhop_external *)(rtm + 1); nh_map[nh_count].idx = nh->nh_idx; nh_map[nh_count].rtm = rtm; nh_count++; } if (nh_count > 0) qsort(nh_map, nh_count, sizeof(struct nhops_map), cmp_nh_idx); nd->nh_buf = buf; nd->nh_count = nh_count; nd->nh_map = nh_map; } static void print_nhops_sysctl(int fibnum, int af) { struct nhops_dump nd; struct nhop_external *nh; int fam; struct rt_msghdr *rtm; dump_nhops_sysctl(fibnum, af, &nd); xo_open_container("nhop-table"); xo_open_list("rt-family"); if (nd.nh_count > 0) { nh = (struct nhop_external *)(nd.nh_map[0].rtm + 1); fam = nh->nh_family; wid_dst = WID_GW_DEFAULT(fam); wid_gw = WID_GW_DEFAULT(fam); wid_nhidx = 5; wid_nhtype = 12; wid_refcnt = 6; wid_flags = 6; wid_pksent = 8; wid_mtu = 6; wid_if = WID_IF_DEFAULT(fam); xo_open_instance("rt-family"); pr_family(fam); xo_open_list("nh-entry"); print_nhop_header(fam); for (size_t i = 0; i < nd.nh_count; i++) { rtm = nd.nh_map[i].rtm; nh = (struct nhop_external *)(rtm + 1); print_nhop_entry_sysctl("nh-entry", rtm, nh); } xo_close_list("nh-entry"); xo_close_instance("rt-family"); } xo_close_list("rt-family"); xo_close_container("nhop-table"); free(nd.nh_buf); } static void p_nhflags(int f, const char *format) { struct bits *p; char *pretty_name = "nh_flags_pretty"; xo_emit(format, fmt_flags(nh_bits, f)); xo_open_list(pretty_name); for (p = nh_bits; p->b_mask; p++) if (p->b_mask & f) xo_emit("{le:nh_flags_pretty/%s}", p->b_name); xo_close_list(pretty_name); } void nhops_print(int fibnum, int af) { size_t intsize; int numfibs; intsize = sizeof(int); if (fibnum == -1 && sysctlbyname("net.my_fibnum", &fibnum, &intsize, NULL, 0) == -1) fibnum = 0; if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1) numfibs = 1; if (fibnum < 0 || fibnum > numfibs - 1) errx(EX_USAGE, "%d: invalid fib", fibnum); ifmap = prepare_ifmap(&ifmap_size); xo_open_container("route-nhop-information"); xo_emit("{T:Nexthop data}"); if (fibnum) xo_emit(" ({L:fib}: {:fib/%d})", fibnum); xo_emit("\n"); print_nhops_sysctl(fibnum, af); xo_close_container("route-nhop-information"); } diff --git a/usr.bin/netstat/pfkey.c b/usr.bin/netstat/pfkey.c index d4ed0c822940..27dd61a0c4e5 100644 --- a/usr.bin/netstat/pfkey.c +++ b/usr.bin/netstat/pfkey.c @@ -1,204 +1,200 @@ /* $NetBSD: inet.c,v 1.35.2.1 1999/04/29 14:57:08 perry Exp $ */ /* $KAME: ipsec.c,v 1.25 2001/03/12 09:04:39 itojun Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. * 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 project 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 PROJECT 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 PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #ifdef IPSEC #include #endif #include #include #include #include #include #include #include "netstat.h" #ifdef IPSEC static const char *pfkey_msgtypenames[] = { "reserved", "getspi", "update", "add", "delete", "get", "acquire", "register", "expire", "flush", "dump", "x_promisc", "x_pchange", "x_spdupdate", "x_spdadd", "x_spddelete", "x_spdget", "x_spdacquire", "x_spddump", "x_spdflush", "x_spdsetidx", "x_spdexpire", "x_spddelete2" }; static const char *pfkey_msgtype_names (int); static const char * pfkey_msgtype_names(int x) { const int max = nitems(pfkey_msgtypenames); static char buf[20]; if (x < max && pfkey_msgtypenames[x]) return pfkey_msgtypenames[x]; snprintf(buf, sizeof(buf), "#%d", x); return buf; } void pfkey_stats(u_long off, const char *name, int family __unused, int proto __unused) { struct pfkeystat pfkeystat; unsigned first, type; if (off == 0) return; xo_emit("{T:/%s}:\n", name); xo_open_container(name); kread_counters(off, (char *)&pfkeystat, sizeof(pfkeystat)); #define p(f, m) if (pfkeystat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)pfkeystat.f, plural(pfkeystat.f)) /* userland -> kernel */ p(out_total, "\t{:sent-requests/%ju} " "{N:/request%s sent from userland}\n"); p(out_bytes, "\t{:sent-bytes/%ju} " "{N:/byte%s sent from userland}\n"); for (first = 1, type = 0; type userland */ p(in_total, "\t{:received-requests/%ju} " "{N:/request%s sent to userland}\n"); p(in_bytes, "\t{:received-bytes/%ju} " "{N:/byte%s sent to userland}\n"); for (first = 1, type = 0; type < sizeof(pfkeystat.in_msgtype)/sizeof(pfkeystat.in_msgtype[0]); type++) { if (pfkeystat.in_msgtype[type] <= 0) continue; if (first) { xo_open_list("input-histogram"); xo_emit("\t{T:histogram by message type}:\n"); first = 0; } xo_open_instance("input-histogram"); xo_emit("\t\t{k:type/%s}: {:count/%ju}\n", pfkey_msgtype_names(type), (uintmax_t)pfkeystat.in_msgtype[type]); xo_close_instance("input-histogram"); } if (!first) xo_close_list("input-histogram"); p(in_msgtarget[KEY_SENDUP_ONE], "\t{:received-one-socket/%ju} " "{N:/message%s toward single socket}\n"); p(in_msgtarget[KEY_SENDUP_ALL], "\t{:received-all-sockets/%ju} " "{N:/message%s toward all sockets}\n"); p(in_msgtarget[KEY_SENDUP_REGISTERED], "\t{:received-registered-sockets/%ju} " "{N:/message%s toward registered sockets}\n"); p(in_nomem, "\t{:discarded-no-memory/%ju} " "{N:/message%s with memory allocation failure}\n"); #undef p xo_close_container(name); } #endif /* IPSEC */ diff --git a/usr.bin/netstat/route.c b/usr.bin/netstat/route.c index 9ef3e658db56..6152cbdc859f 100644 --- a/usr.bin/netstat/route.c +++ b/usr.bin/netstat/route.c @@ -1,731 +1,727 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" #include "common.h" #include "nl_defs.h" /* * Definitions for showing gateway flags. */ struct bits rt_bits[] = { { RTF_UP, 'U', "up" }, { RTF_GATEWAY, 'G', "gateway" }, { RTF_HOST, 'H', "host" }, { RTF_REJECT, 'R', "reject" }, { RTF_DYNAMIC, 'D', "dynamic" }, { RTF_MODIFIED, 'M', "modified" }, { RTF_DONE, 'd', "done" }, /* Completed -- for routing msgs only */ { RTF_XRESOLVE, 'X', "xresolve" }, { RTF_STATIC, 'S', "static" }, { RTF_PROTO1, '1', "proto1" }, { RTF_PROTO2, '2', "proto2" }, { RTF_PROTO3, '3', "proto3" }, { RTF_BLACKHOLE,'B', "blackhole" }, { RTF_BROADCAST,'b', "broadcast" }, #ifdef RTF_LLINFO { RTF_LLINFO, 'L', "llinfo" }, #endif { 0 , 0, NULL } }; #ifdef WITHOUT_NETLINK static struct ifmap_entry *ifmap; static size_t ifmap_size; #endif static struct timespec uptime; static const char *netname4(in_addr_t, in_addr_t); #ifdef INET6 static const char *netname6(struct sockaddr_in6 *, struct sockaddr_in6 *); #endif #ifdef WITHOUT_NETLINK static void p_rtable_sysctl(int, int); static void p_rtentry_sysctl(const char *name, struct rt_msghdr *); #endif static void domask(char *, size_t, u_long); const uint32_t rt_default_weight = RT_DEFAULT_WEIGHT; /* * Print routing tables. */ void routepr(int fibnum, int af) { size_t intsize; int numfibs; if (live == 0) return; intsize = sizeof(int); if (fibnum == -1 && sysctlbyname("net.my_fibnum", &fibnum, &intsize, NULL, 0) == -1) fibnum = 0; if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1) numfibs = 1; if (fibnum < 0 || fibnum > numfibs - 1) errx(EX_USAGE, "%d: invalid fib", fibnum); /* * Since kernel & userland use different timebase * (time_uptime vs time_second) and we are reading kernel memory * directly we should do rt_expire --> expire_time conversion. */ if (clock_gettime(CLOCK_UPTIME, &uptime) < 0) err(EX_OSERR, "clock_gettime() failed"); xo_open_container("route-information"); xo_emit("{T:Routing tables}"); if (fibnum) xo_emit(" ({L:fib}: {:fib/%d})", fibnum); xo_emit("\n"); #ifdef WITHOUT_NETLINK p_rtable_sysctl(fibnum, af); #else p_rtable_netlink(fibnum, af); #endif xo_close_container("route-information"); } /* * Print address family header before a section of the routing table. */ void pr_family(int af1) { const char *afname; switch (af1) { case AF_INET: afname = "Internet"; break; #ifdef INET6 case AF_INET6: afname = "Internet6"; break; #endif /*INET6*/ case AF_ISO: afname = "ISO"; break; case AF_CCITT: afname = "X.25"; break; case AF_NETGRAPH: afname = "Netgraph"; break; default: afname = NULL; break; } if (afname) xo_emit("\n{k:address-family/%s}:\n", afname); else xo_emit("\n{L:Protocol Family} {k:address-family/%d}:\n", af1); } /* column widths; each followed by one space */ #ifndef INET6 #define WID_DST_DEFAULT(af) 18 /* width of destination column */ #define WID_GW_DEFAULT(af) 18 /* width of gateway column */ #define WID_IF_DEFAULT(af) (Wflag ? 10 : 8) /* width of netif column */ #else #define WID_DST_DEFAULT(af) \ ((af) == AF_INET6 ? (numeric_addr ? 33: 18) : 18) #define WID_GW_DEFAULT(af) \ ((af) == AF_INET6 ? (numeric_addr ? 29 : 18) : 18) #define WID_IF_DEFAULT(af) ((af) == AF_INET6 ? 8 : (Wflag ? 10 : 8)) #endif /*INET6*/ struct _wid wid; /* * Print header for routing table columns. */ void pr_rthdr(int af1 __unused) { if (Wflag) { xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} " "{T:/%*.*s} {T:/%*.*s} {T:/%*s}\n", wid.dst, wid.dst, "Destination", wid.gw, wid.gw, "Gateway", wid.flags, wid.flags, "Flags", wid.mtu, wid.mtu, "Nhop#", wid.mtu, wid.mtu, "Mtu", wid.iface, wid.iface, "Netif", wid.expire, "Expire"); } else { xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} " "{T:/%*s}\n", wid.dst, wid.dst, "Destination", wid.gw, wid.gw, "Gateway", wid.flags, wid.flags, "Flags", wid.iface, wid.iface, "Netif", wid.expire, "Expire"); } } void set_wid(int fam) { wid.dst = WID_DST_DEFAULT(fam); wid.gw = WID_GW_DEFAULT(fam); wid.flags = 6; wid.pksent = 8; wid.mtu = 6; wid.iface = WID_IF_DEFAULT(fam); wid.expire = 6; } #ifdef WITHOUT_NETLINK static void p_rtable_sysctl(int fibnum, int af) { size_t needed; int mib[7]; char *buf, *next, *lim; struct rt_msghdr *rtm; struct sockaddr *sa; int fam = AF_UNSPEC; int need_table_close = false; ifmap = prepare_ifmap(&ifmap_size); mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = af; mib[4] = NET_RT_DUMP; mib[5] = 0; mib[6] = fibnum; if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0) err(EX_OSERR, "sysctl: net.route.0.%d.dump.%d estimate", af, fibnum); if ((buf = malloc(needed)) == NULL) errx(2, "malloc(%lu)", (unsigned long)needed); if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) err(1, "sysctl: net.route.0.%d.dump.%d", af, fibnum); lim = buf + needed; xo_open_container("route-table"); xo_open_list("rt-family"); for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; if (rtm->rtm_version != RTM_VERSION) continue; /* * Peek inside header to determine AF */ sa = (struct sockaddr *)(rtm + 1); /* Only print family first time. */ if (fam != sa->sa_family) { if (need_table_close) { xo_close_list("rt-entry"); xo_close_instance("rt-family"); } need_table_close = true; fam = sa->sa_family; set_wid(fam); xo_open_instance("rt-family"); pr_family(fam); xo_open_list("rt-entry"); pr_rthdr(fam); } p_rtentry_sysctl("rt-entry", rtm); } if (need_table_close) { xo_close_list("rt-entry"); xo_close_instance("rt-family"); } xo_close_list("rt-family"); xo_close_container("route-table"); free(buf); } static void p_rtentry_sysctl(const char *name, struct rt_msghdr *rtm) { struct sockaddr *sa, *addr[RTAX_MAX]; char buffer[128]; char prettyname[128]; int i, protrusion; xo_open_instance(name); sa = (struct sockaddr *)(rtm + 1); for (i = 0; i < RTAX_MAX; i++) { if (rtm->rtm_addrs & (1 << i)) { addr[i] = sa; sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); } } protrusion = p_sockaddr("destination", addr[RTAX_DST], addr[RTAX_NETMASK], rtm->rtm_flags, wid.dst); protrusion = p_sockaddr("gateway", addr[RTAX_GATEWAY], NULL, RTF_HOST, wid.gw - protrusion); snprintf(buffer, sizeof(buffer), "{[:-%d}{:flags/%%s}{]:} ", wid.flags - protrusion); p_flags(rtm->rtm_flags, buffer); /* Output path weight as non-visual property */ xo_emit("{e:weight/%u}", rtm->rtm_rmx.rmx_weight); if (Wflag) { /* XXX: use=0? */ xo_emit("{t:nhop/%*lu} ", wid.mtu, rtm->rtm_rmx.rmx_nhidx); if (rtm->rtm_rmx.rmx_mtu != 0) xo_emit("{t:mtu/%*lu} ", wid.mtu, rtm->rtm_rmx.rmx_mtu); else xo_emit("{P:/%*s} ", wid.mtu, ""); } memset(prettyname, 0, sizeof(prettyname)); if (rtm->rtm_index < ifmap_size) { strlcpy(prettyname, ifmap[rtm->rtm_index].ifname, sizeof(prettyname)); if (*prettyname == '\0') strlcpy(prettyname, "---", sizeof(prettyname)); } if (Wflag) xo_emit("{t:interface-name/%*s}", wid.iface, prettyname); else xo_emit("{t:interface-name/%*.*s}", wid.iface, wid.iface, prettyname); if (rtm->rtm_rmx.rmx_expire) { time_t expire_time; if ((expire_time = rtm->rtm_rmx.rmx_expire - uptime.tv_sec) > 0) xo_emit(" {:expire-time/%*d}", wid.expire, (int)expire_time); } xo_emit("\n"); xo_close_instance(name); } #endif int p_sockaddr(const char *name, struct sockaddr *sa, struct sockaddr *mask, int flags, int width) { const char *cp; char buf[128]; int protrusion; cp = fmt_sockaddr(sa, mask, flags); if (width < 0) { snprintf(buf, sizeof(buf), "{:%s/%%s} ", name); xo_emit(buf, cp); protrusion = 0; } else { if (Wflag != 0 || numeric_addr) { snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%s}{]:} ", -width, name); xo_emit(buf, cp); protrusion = strlen(cp) - width; if (protrusion < 0) protrusion = 0; } else { snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%-.*s}{]:} ", -width, name); xo_emit(buf, width, cp); protrusion = 0; } } return (protrusion); } const char * fmt_sockaddr(struct sockaddr *sa, struct sockaddr *mask, int flags) { static char buf[128]; const char *cp; if (sa == NULL) return ("null"); switch(sa->sa_family) { #ifdef INET6 case AF_INET6: /* * The sa6->sin6_scope_id must be filled here because * this sockaddr is extracted from kmem(4) directly * and has KAME-specific embedded scope id in * sa6->sin6_addr.s6_addr[2]. */ in6_fillscopeid(satosin6(sa)); /* FALLTHROUGH */ #endif /*INET6*/ case AF_INET: if (flags & RTF_HOST) cp = routename(sa, numeric_addr); else if (mask) cp = netname(sa, mask); else cp = netname(sa, NULL); break; case AF_NETGRAPH: { strlcpy(buf, ((struct sockaddr_ng *)sa)->sg_data, sizeof(buf)); cp = buf; break; } case AF_LINK: { #if 0 struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; /* Interface route. */ if (sdl->sdl_nlen) cp = sdl->sdl_data; else #endif cp = routename(sa, 1); break; } default: { u_char *s = (u_char *)sa->sa_data, *slim; char *cq, *cqlim; cq = buf; slim = sa->sa_len + (u_char *) sa; cqlim = cq + sizeof(buf) - sizeof(" ffff"); snprintf(cq, sizeof(buf), "(%d)", sa->sa_family); cq += strlen(cq); while (s < slim && cq < cqlim) { snprintf(cq, sizeof(" ff"), " %02x", *s++); cq += strlen(cq); if (s < slim) { snprintf(cq, sizeof("ff"), "%02x", *s++); cq += strlen(cq); } } cp = buf; } } return (cp); } void p_flags(int f, const char *format) { print_flags_generic(f, rt_bits, format, "flags_pretty"); } char * routename(struct sockaddr *sa, int flags) { static char line[NI_MAXHOST]; int error, f; f = (flags) ? NI_NUMERICHOST : 0; error = getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0, f); if (error) { const void *src; switch (sa->sa_family) { #ifdef INET case AF_INET: src = &satosin(sa)->sin_addr; break; #endif /* INET */ #ifdef INET6 case AF_INET6: src = &satosin6(sa)->sin6_addr; break; #endif /* INET6 */ default: return(line); } inet_ntop(sa->sa_family, src, line, sizeof(line) - 1); return (line); } trimdomain(line, strlen(line)); return (line); } #define NSHIFT(m) ( \ (m) == IN_CLASSA_NET ? IN_CLASSA_NSHIFT : \ (m) == IN_CLASSB_NET ? IN_CLASSB_NSHIFT : \ (m) == IN_CLASSC_NET ? IN_CLASSC_NSHIFT : \ 0) static void domask(char *dst, size_t buflen, u_long mask) { int b, i; if (mask == 0) { *dst = '\0'; return; } i = 0; for (b = 0; b < 32; b++) if (mask & (1 << b)) { int bb; i = b; for (bb = b+1; bb < 32; bb++) if (!(mask & (1 << bb))) { i = -1; /* noncontig */ break; } break; } if (i == -1) snprintf(dst, buflen, "&0x%lx", mask); else snprintf(dst, buflen, "/%d", 32-i); } /* * Return the name of the network whose address is given. */ const char * netname(struct sockaddr *sa, struct sockaddr *mask) { switch (sa->sa_family) { case AF_INET: if (mask != NULL) return (netname4(satosin(sa)->sin_addr.s_addr, satosin(mask)->sin_addr.s_addr)); else return (netname4(satosin(sa)->sin_addr.s_addr, INADDR_ANY)); break; #ifdef INET6 case AF_INET6: return (netname6(satosin6(sa), satosin6(mask))); #endif /* INET6 */ default: return (NULL); } } static const char * netname4(in_addr_t in, in_addr_t mask) { char *cp = 0; static char line[MAXHOSTNAMELEN + sizeof("&0xffffffff")]; char nline[INET_ADDRSTRLEN]; struct netent *np = 0; in_addr_t i; if (in == INADDR_ANY && mask == 0) { strlcpy(line, "default", sizeof(line)); return (line); } /* It is ok to supply host address. */ in &= mask; i = ntohl(in); if (!numeric_addr && i) { np = getnetbyaddr(i >> NSHIFT(ntohl(mask)), AF_INET); if (np != NULL) { cp = np->n_name; trimdomain(cp, strlen(cp)); } } if (cp != NULL) strlcpy(line, cp, sizeof(line)); else { inet_ntop(AF_INET, &in, nline, sizeof(nline)); strlcpy(line, nline, sizeof(line)); domask(line + strlen(line), sizeof(line) - strlen(line), ntohl(mask)); } return (line); } #undef NSHIFT #ifdef INET6 void in6_fillscopeid(struct sockaddr_in6 *sa6) { #if defined(__KAME__) /* * XXX: This is a special workaround for KAME kernels. * sin6_scope_id field of SA should be set in the future. */ if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) || IN6_IS_ADDR_MC_NODELOCAL(&sa6->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sa6->sin6_addr)) { if (sa6->sin6_scope_id == 0) sa6->sin6_scope_id = ntohs(*(u_int16_t *)&sa6->sin6_addr.s6_addr[2]); sa6->sin6_addr.s6_addr[2] = sa6->sin6_addr.s6_addr[3] = 0; } #endif } /* Mask to length table. To check an invalid value, (length + 1) is used. */ static const u_char masktolen[256] = { [0xff] = 8 + 1, [0xfe] = 7 + 1, [0xfc] = 6 + 1, [0xf8] = 5 + 1, [0xf0] = 4 + 1, [0xe0] = 3 + 1, [0xc0] = 2 + 1, [0x80] = 1 + 1, [0x00] = 0 + 1, }; static const char * netname6(struct sockaddr_in6 *sa6, struct sockaddr_in6 *mask) { static char line[NI_MAXHOST + sizeof("/xxx") - 1]; struct sockaddr_in6 addr; char nline[NI_MAXHOST]; char maskbuf[sizeof("/xxx")]; u_char *p, *lim; u_char masklen; int i; bool illegal = false; if (mask) { p = (u_char *)&mask->sin6_addr; for (masklen = 0, lim = p + 16; p < lim; p++) { if (masktolen[*p] > 0) { /* -1 is required. */ masklen += (masktolen[*p] - 1); } else illegal = true; } if (illegal) xo_error("illegal prefixlen\n"); memcpy(&addr, sa6, sizeof(addr)); for (i = 0; i < 16; ++i) addr.sin6_addr.s6_addr[i] &= mask->sin6_addr.s6_addr[i]; sa6 = &addr; } else masklen = 128; if (masklen == 0 && IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr)) return("default"); getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, nline, sizeof(nline), NULL, 0, NI_NUMERICHOST); if (numeric_addr) strlcpy(line, nline, sizeof(line)); else getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, line, sizeof(line), NULL, 0, 0); if (numeric_addr || strcmp(line, nline) == 0) { snprintf(maskbuf, sizeof(maskbuf), "/%d", masklen); strlcat(line, maskbuf, sizeof(line)); } return (line); } #endif /*INET6*/ /* * Print routing statistics */ void rt_stats(void) { struct rtstat rtstat; u_long rtsaddr; if ((rtsaddr = nl[N_RTSTAT].n_value) == 0) { xo_emit("{W:rtstat: symbol not in namelist}\n"); return; } kread_counters(rtsaddr, (char *)&rtstat, sizeof (rtstat)); xo_emit("{T:routing}:\n"); #define p(f, m) if (rtstat.f || sflag <= 1) \ xo_emit(m, rtstat.f, plural(rtstat.f)) p(rts_badredirect, "\t{:bad-redirects/%ju} " "{N:/bad routing redirect%s}\n"); p(rts_dynamic, "\t{:dynamically-created/%ju} " "{N:/dynamically created route%s}\n"); p(rts_newgateway, "\t{:new-gateways/%ju} " "{N:/new gateway%s due to redirects}\n"); p(rts_unreach, "\t{:unreachable-destination/%ju} " "{N:/destination%s found unreachable}\n"); p(rts_wildcard, "\t{:wildcard-uses/%ju} " "{N:/use%s of a wildcard route}\n"); #undef p } diff --git a/usr.bin/netstat/route_netlink.c b/usr.bin/netstat/route_netlink.c index 796f8d3eca68..74864fab76a7 100644 --- a/usr.bin/netstat/route_netlink.c +++ b/usr.bin/netstat/route_netlink.c @@ -1,342 +1,341 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" #include "common.h" #include "nl_defs.h" static void p_rtentry_netlink(struct snl_state *ss, const char *name, struct nlmsghdr *hdr); static struct ifmap_entry *ifmap; static size_t ifmap_size; /* Generate ifmap using netlink */ static struct ifmap_entry * prepare_ifmap_netlink(struct snl_state *ss, size_t *pifmap_size) { struct { struct nlmsghdr hdr; struct ifinfomsg ifmsg; } msg = { .hdr.nlmsg_type = RTM_GETLINK, .hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, .hdr.nlmsg_seq = snl_get_seq(ss), }; msg.hdr.nlmsg_len = sizeof(msg); if (!snl_send_message(ss, &msg.hdr)) return (NULL); struct ifmap_entry *ifmap = NULL; uint32_t ifmap_size = 0; struct nlmsghdr *hdr; struct snl_errmsg_data e = {}; while ((hdr = snl_read_reply_multi(ss, msg.hdr.nlmsg_seq, &e)) != NULL) { struct snl_parsed_link_simple link = {}; if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, &link)) continue; if (link.ifi_index >= ifmap_size) { size_t size = roundup2(link.ifi_index + 1, 32) * sizeof(struct ifmap_entry); if ((ifmap = realloc(ifmap, size)) == NULL) errx(2, "realloc(%zu) failed", size); memset(&ifmap[ifmap_size], 0, size - ifmap_size * sizeof(struct ifmap_entry)); ifmap_size = roundup2(link.ifi_index + 1, 32); } if (*ifmap[link.ifi_index].ifname != '\0') continue; strlcpy(ifmap[link.ifi_index].ifname, link.ifla_ifname, IFNAMSIZ); ifmap[link.ifi_index].mtu = link.ifla_mtu; } *pifmap_size = ifmap_size; return (ifmap); } static void ip6_writemask(struct in6_addr *addr6, uint8_t mask) { uint32_t *cp; for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32) *cp++ = 0xFFFFFFFF; if (mask > 0) *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); } static void gen_mask(int family, int plen, struct sockaddr *sa) { if (family == AF_INET6) { struct sockaddr_in6 sin6 = { .sin6_family = AF_INET6, .sin6_len = sizeof(struct sockaddr_in6), }; ip6_writemask(&sin6.sin6_addr, plen); *((struct sockaddr_in6 *)sa) = sin6; } else if (family == AF_INET) { struct sockaddr_in sin = { .sin_family = AF_INET, .sin_len = sizeof(struct sockaddr_in), .sin_addr.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0), }; *((struct sockaddr_in *)sa) = sin; } } static void add_scopeid(struct sockaddr *sa, int ifindex) { if (sa->sa_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) sin6->sin6_scope_id = ifindex; } } static void p_path(struct snl_parsed_route *rt, bool is_mpath) { struct sockaddr_in6 mask6; struct sockaddr *pmask = (struct sockaddr *)&mask6; char buffer[128]; char prettyname[128]; int protrusion; gen_mask(rt->rtm_family, rt->rtm_dst_len, pmask); add_scopeid(rt->rta_dst, rt->rta_oif); add_scopeid(rt->rta_gw, rt->rta_oif); protrusion = p_sockaddr("destination", rt->rta_dst, pmask, rt->rta_rtflags, wid.dst); protrusion = p_sockaddr("gateway", rt->rta_gw, NULL, RTF_HOST, wid.gw - protrusion); snprintf(buffer, sizeof(buffer), "{[:-%d}{:flags/%%s}{]:} ", wid.flags - protrusion); p_flags(rt->rta_rtflags | RTF_UP, buffer); /* Output path weight as non-visual property */ xo_emit("{e:weight/%u}", rt->rtax_weight); if (is_mpath) xo_emit("{e:nhg-kidx/%u}", rt->rta_knh_id); else xo_emit("{e:nhop-kidx/%u}", rt->rta_knh_id); if (rt->rta_nh_id != 0) { if (is_mpath) xo_emit("{e:nhg-uidx/%u}", rt->rta_nh_id); else xo_emit("{e:nhop-uidx/%u}", rt->rta_nh_id); } memset(prettyname, 0, sizeof(prettyname)); if (rt->rta_oif < ifmap_size) { strlcpy(prettyname, ifmap[rt->rta_oif].ifname, sizeof(prettyname)); if (*prettyname == '\0') strlcpy(prettyname, "---", sizeof(prettyname)); if (rt->rtax_mtu == 0) rt->rtax_mtu = ifmap[rt->rta_oif].mtu; } if (Wflag) { /* XXX: use=0? */ xo_emit("{t:nhop/%*lu} ", wid.mtu, is_mpath ? 0 : rt->rta_knh_id); if (rt->rtax_mtu != 0) xo_emit("{t:mtu/%*lu} ", wid.mtu, rt->rtax_mtu); else { /* use interface mtu */ xo_emit("{P:/%*s} ", wid.mtu, ""); } } if (Wflag) xo_emit("{t:interface-name/%*s}", wid.iface, prettyname); else xo_emit("{t:interface-name/%*.*s}", wid.iface, wid.iface, prettyname); if (rt->rta_expires > 0) { xo_emit(" {:expire-time/%*u}", wid.expire, rt->rta_expires); } } static void p_rtentry_netlink(struct snl_state *ss, const char *name, struct nlmsghdr *hdr) { struct snl_parsed_route rt = {}; if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &rt)) return; if (rt.rtax_weight == 0) rt.rtax_weight = rt_default_weight; if (rt.rta_multipath.num_nhops != 0) { uint32_t orig_rtflags = rt.rta_rtflags; uint32_t orig_mtu = rt.rtax_mtu; for (uint32_t i = 0; i < rt.rta_multipath.num_nhops; i++) { struct rta_mpath_nh *nhop = rt.rta_multipath.nhops[i]; rt.rta_gw = nhop->gw; rt.rta_oif = nhop->ifindex; rt.rtax_weight = nhop->rtnh_weight; rt.rta_rtflags = nhop->rta_rtflags ? nhop->rta_rtflags : orig_rtflags; rt.rtax_mtu = nhop->rtax_mtu ? nhop->rtax_mtu : orig_mtu; xo_open_instance(name); p_path(&rt, true); xo_emit("\n"); xo_close_instance(name); } return; } struct sockaddr_dl sdl_gw = { .sdl_family = AF_LINK, .sdl_len = sizeof(struct sockaddr_dl), .sdl_index = rt.rta_oif, }; if (rt.rta_gw == NULL) rt.rta_gw = (struct sockaddr *)&sdl_gw; xo_open_instance(name); p_path(&rt, false); xo_emit("\n"); xo_close_instance(name); } bool p_rtable_netlink(int fibnum, int af) { int fam = AF_UNSPEC; int need_table_close = false; struct nlmsghdr *hdr; struct snl_errmsg_data e = {}; struct snl_state ss = {}; if (!snl_init(&ss, NETLINK_ROUTE)) return (false); ifmap = prepare_ifmap_netlink(&ss, &ifmap_size); if (ifmap == NULL) { snl_free(&ss); return (false); } struct { struct nlmsghdr hdr; struct rtmsg rtmsg; struct nlattr nla_fibnum; uint32_t fibnum; } msg = { .hdr.nlmsg_type = RTM_GETROUTE, .hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, .hdr.nlmsg_seq = snl_get_seq(&ss), .rtmsg.rtm_family = af, .nla_fibnum.nla_len = sizeof(struct nlattr) + sizeof(uint32_t), .nla_fibnum.nla_type = RTA_TABLE, .fibnum = fibnum, }; msg.hdr.nlmsg_len = sizeof(msg); if (!snl_send_message(&ss, &msg.hdr)) { snl_free(&ss); return (false); } xo_open_container("route-table"); xo_open_list("rt-family"); while ((hdr = snl_read_reply_multi(&ss, msg.hdr.nlmsg_seq, &e)) != NULL) { struct rtmsg *rtm = (struct rtmsg *)(hdr + 1); /* Only print family first time. */ if (fam != rtm->rtm_family) { if (need_table_close) { xo_close_list("rt-entry"); xo_close_instance("rt-family"); } need_table_close = true; fam = rtm->rtm_family; set_wid(fam); xo_open_instance("rt-family"); pr_family(fam); xo_open_list("rt-entry"); pr_rthdr(fam); } p_rtentry_netlink(&ss, "rt-entry", hdr); snl_clear_lb(&ss); } if (need_table_close) { xo_close_list("rt-entry"); xo_close_instance("rt-family"); } xo_close_list("rt-family"); xo_close_container("route-table"); snl_free(&ss); return (true); } diff --git a/usr.bin/netstat/sctp.c b/usr.bin/netstat/sctp.c index 5395cad9fe7d..92c4bf67b2fa 100644 --- a/usr.bin/netstat/sctp.c +++ b/usr.bin/netstat/sctp.c @@ -1,838 +1,834 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2001-2007, by Weongyo Jeong. All rights reserved. * Copyright (c) 2011, by Michael Tuexen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * a) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * b) 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. * * c) Neither the name of Cisco Systems, Inc. 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" #include #ifdef SCTP static void sctp_statesprint(uint32_t state); #define NETSTAT_SCTP_STATES_CLOSED 0x0 #define NETSTAT_SCTP_STATES_BOUND 0x1 #define NETSTAT_SCTP_STATES_LISTEN 0x2 #define NETSTAT_SCTP_STATES_COOKIE_WAIT 0x3 #define NETSTAT_SCTP_STATES_COOKIE_ECHOED 0x4 #define NETSTAT_SCTP_STATES_ESTABLISHED 0x5 #define NETSTAT_SCTP_STATES_SHUTDOWN_SENT 0x6 #define NETSTAT_SCTP_STATES_SHUTDOWN_RECEIVED 0x7 #define NETSTAT_SCTP_STATES_SHUTDOWN_ACK_SENT 0x8 #define NETSTAT_SCTP_STATES_SHUTDOWN_PENDING 0x9 static const char *sctpstates[] = { "CLOSED", "BOUND", "LISTEN", "COOKIE_WAIT", "COOKIE_ECHOED", "ESTABLISHED", "SHUTDOWN_SENT", "SHUTDOWN_RECEIVED", "SHUTDOWN_ACK_SENT", "SHUTDOWN_PENDING" }; static LIST_HEAD(xladdr_list, xladdr_entry) xladdr_head; struct xladdr_entry { struct xsctp_laddr *xladdr; LIST_ENTRY(xladdr_entry) xladdr_entries; }; static LIST_HEAD(xraddr_list, xraddr_entry) xraddr_head; struct xraddr_entry { struct xsctp_raddr *xraddr; LIST_ENTRY(xraddr_entry) xraddr_entries; }; static void sctp_print_address(const char *container, union sctp_sockstore *address, int port, int num_port) { struct servent *sp = 0; char line[80], *cp; int width; size_t alen, plen; if (container) xo_open_container(container); switch (address->sa.sa_family) { #ifdef INET case AF_INET: snprintf(line, sizeof(line), "%.*s.", Wflag ? 39 : 16, inetname(&address->sin.sin_addr)); break; #endif #ifdef INET6 case AF_INET6: snprintf(line, sizeof(line), "%.*s.", Wflag ? 39 : 16, inet6name(&address->sin6.sin6_addr)); break; #endif default: snprintf(line, sizeof(line), "%.*s.", Wflag ? 39 : 16, ""); break; } alen = strlen(line); cp = line + alen; if (!num_port && port) sp = getservbyport((int)port, "sctp"); if (sp || port == 0) snprintf(cp, sizeof(line) - alen, "%.15s ", sp ? sp->s_name : "*"); else snprintf(cp, sizeof(line) - alen, "%d ", ntohs((u_short)port)); width = Wflag ? 45 : 22; xo_emit("{d:target/%-*.*s} ", width, width, line); plen = strlen(cp) - 1; alen--; xo_emit("{e:address/%*.*s}{e:port/%*.*s}", alen, alen, line, plen, plen, cp); if (container) xo_close_container(container); } static int sctp_skip_xinpcb_ifneed(char *buf, const size_t buflen, size_t *offset) { int exist_tcb = 0; struct xsctp_tcb *xstcb; struct xsctp_raddr *xraddr; struct xsctp_laddr *xladdr; while (*offset < buflen) { xladdr = (struct xsctp_laddr *)(buf + *offset); *offset += sizeof(struct xsctp_laddr); if (xladdr->last == 1) break; } while (*offset < buflen) { xstcb = (struct xsctp_tcb *)(buf + *offset); *offset += sizeof(struct xsctp_tcb); if (xstcb->last == 1) break; exist_tcb = 1; while (*offset < buflen) { xladdr = (struct xsctp_laddr *)(buf + *offset); *offset += sizeof(struct xsctp_laddr); if (xladdr->last == 1) break; } while (*offset < buflen) { xraddr = (struct xsctp_raddr *)(buf + *offset); *offset += sizeof(struct xsctp_raddr); if (xraddr->last == 1) break; } } /* * If Lflag is set, we don't care about the return value. */ if (Lflag) return 0; return exist_tcb; } static void sctp_process_tcb(struct xsctp_tcb *xstcb, char *buf, const size_t buflen, size_t *offset, int *indent) { int i, xl_total = 0, xr_total = 0, x_max; struct xsctp_raddr *xraddr; struct xsctp_laddr *xladdr; struct xladdr_entry *prev_xl = NULL, *xl = NULL, *xl_tmp; struct xraddr_entry *prev_xr = NULL, *xr = NULL, *xr_tmp; LIST_INIT(&xladdr_head); LIST_INIT(&xraddr_head); /* * Make `struct xladdr_list' list and `struct xraddr_list' list * to handle the address flexibly. */ while (*offset < buflen) { xladdr = (struct xsctp_laddr *)(buf + *offset); *offset += sizeof(struct xsctp_laddr); if (xladdr->last == 1) break; prev_xl = xl; xl = malloc(sizeof(struct xladdr_entry)); if (xl == NULL) { xo_warnx("malloc %lu bytes", (u_long)sizeof(struct xladdr_entry)); goto out; } xl->xladdr = xladdr; if (prev_xl == NULL) LIST_INSERT_HEAD(&xladdr_head, xl, xladdr_entries); else LIST_INSERT_AFTER(prev_xl, xl, xladdr_entries); xl_total++; } while (*offset < buflen) { xraddr = (struct xsctp_raddr *)(buf + *offset); *offset += sizeof(struct xsctp_raddr); if (xraddr->last == 1) break; prev_xr = xr; xr = malloc(sizeof(struct xraddr_entry)); if (xr == NULL) { xo_warnx("malloc %lu bytes", (u_long)sizeof(struct xraddr_entry)); goto out; } xr->xraddr = xraddr; if (prev_xr == NULL) LIST_INSERT_HEAD(&xraddr_head, xr, xraddr_entries); else LIST_INSERT_AFTER(prev_xr, xr, xraddr_entries); xr_total++; } /* * Let's print the address infos. */ xo_open_list("address"); xl = LIST_FIRST(&xladdr_head); xr = LIST_FIRST(&xraddr_head); x_max = MAX(xl_total, xr_total); for (i = 0; i < x_max; i++) { xo_open_instance("address"); if (((*indent == 0) && i > 0) || *indent > 0) xo_emit("{P:/%-12s} ", " "); if (xl != NULL) { sctp_print_address("local", &(xl->xladdr->address), htons(xstcb->local_port), numeric_port); } else { if (Wflag) { xo_emit("{P:/%-45s} ", " "); } else { xo_emit("{P:/%-22s} ", " "); } } if (xr != NULL && !Lflag) { sctp_print_address("remote", &(xr->xraddr->address), htons(xstcb->remote_port), numeric_port); } if (xl != NULL) xl = LIST_NEXT(xl, xladdr_entries); if (xr != NULL) xr = LIST_NEXT(xr, xraddr_entries); if (i == 0 && !Lflag) sctp_statesprint(xstcb->state); if (i < x_max) xo_emit("\n"); xo_close_instance("address"); } out: /* * Free the list which be used to handle the address. */ xl = LIST_FIRST(&xladdr_head); while (xl != NULL) { xl_tmp = LIST_NEXT(xl, xladdr_entries); free(xl); xl = xl_tmp; } xr = LIST_FIRST(&xraddr_head); while (xr != NULL) { xr_tmp = LIST_NEXT(xr, xraddr_entries); free(xr); xr = xr_tmp; } } static void sctp_process_inpcb(struct xsctp_inpcb *xinpcb, char *buf, const size_t buflen, size_t *offset) { int indent = 0, xladdr_total = 0, is_listening = 0; static int first = 1; const char *tname, *pname; struct xsctp_tcb *xstcb; struct xsctp_laddr *xladdr; size_t offset_laddr; int process_closed; if (xinpcb->maxqlen > 0) is_listening = 1; if (first) { if (!Lflag) { xo_emit("Active SCTP associations"); if (aflag) xo_emit(" (including servers)"); } else xo_emit("Current listen queue sizes (qlen/maxqlen)"); xo_emit("\n"); if (Lflag) xo_emit("{T:/%-6.6s} {T:/%-5.5s} {T:/%-8.8s} " "{T:/%-22.22s}\n", "Proto", "Type", "Listen", "Local Address"); else if (Wflag) xo_emit("{T:/%-6.6s} {T:/%-5.5s} {T:/%-45.45s} " "{T:/%-45.45s} {T:/%s}\n", "Proto", "Type", "Local Address", "Foreign Address", "(state)"); else xo_emit("{T:/%-6.6s} {T:/%-5.5s} {T:/%-22.22s} " "{T:/%-22.22s} {T:/%s}\n", "Proto", "Type", "Local Address", "Foreign Address", "(state)"); first = 0; } xladdr = (struct xsctp_laddr *)(buf + *offset); if ((!aflag && is_listening) || (Lflag && !is_listening)) { sctp_skip_xinpcb_ifneed(buf, buflen, offset); return; } if (xinpcb->flags & SCTP_PCB_FLAGS_BOUND_V6) { /* Can't distinguish between sctp46 and sctp6 */ pname = "sctp46"; } else { pname = "sctp4"; } if (xinpcb->flags & SCTP_PCB_FLAGS_TCPTYPE) tname = "1to1"; else if (xinpcb->flags & SCTP_PCB_FLAGS_UDPTYPE) tname = "1toN"; else tname = "????"; if (Lflag) { char buf1[22]; snprintf(buf1, sizeof buf1, "%u/%u", xinpcb->qlen, xinpcb->maxqlen); xo_emit("{:protocol/%-6.6s/%s} {:type/%-5.5s/%s} ", pname, tname); xo_emit("{d:queues/%-8.8s}{e:queue-len/%hu}" "{e:max-queue-len/%hu} ", buf1, xinpcb->qlen, xinpcb->maxqlen); } offset_laddr = *offset; process_closed = 0; xo_open_list("local-address"); retry: while (*offset < buflen) { xladdr = (struct xsctp_laddr *)(buf + *offset); *offset += sizeof(struct xsctp_laddr); if (xladdr->last) { if (aflag && !Lflag && (xladdr_total == 0) && process_closed) { xo_open_instance("local-address"); xo_emit("{:protocol/%-6.6s/%s} " "{:type/%-5.5s/%s} ", pname, tname); if (Wflag) { xo_emit("{P:/%-91.91s/%s} " "{:state/CLOSED}", " "); } else { xo_emit("{P:/%-45.45s/%s} " "{:state/CLOSED}", " "); } xo_close_instance("local-address"); } if (process_closed || is_listening) { xo_emit("\n"); } break; } if (!Lflag && !is_listening && !process_closed) continue; xo_open_instance("local-address"); if (xladdr_total == 0) { if (!Lflag) { xo_emit("{:protocol/%-6.6s/%s} " "{:type/%-5.5s/%s} ", pname, tname); } } else { xo_emit("\n"); xo_emit(Lflag ? "{P:/%-21.21s} " : "{P:/%-12.12s} ", " "); } sctp_print_address("local", &(xladdr->address), htons(xinpcb->local_port), numeric_port); if (aflag && !Lflag && xladdr_total == 0) { if (Wflag) { if (process_closed) { xo_emit("{P:/%-45.45s} " "{:state/CLOSED}", " "); } else { xo_emit("{P:/%-45.45s} " "{:state/LISTEN}", " "); } } else { if (process_closed) { xo_emit("{P:/%-22.22s} " "{:state/CLOSED}", " "); } else { xo_emit("{P:/%-22.22s} " "{:state/LISTEN}", " "); } } } xladdr_total++; xo_close_instance("local-address"); } xstcb = (struct xsctp_tcb *)(buf + *offset); *offset += sizeof(struct xsctp_tcb); if (aflag && (xladdr_total == 0) && xstcb->last && !process_closed) { process_closed = 1; *offset = offset_laddr; goto retry; } while (xstcb->last == 0 && *offset < buflen) { xo_emit("{:protocol/%-6.6s/%s} {:type/%-5.5s/%s} ", pname, tname); sctp_process_tcb(xstcb, buf, buflen, offset, &indent); indent++; xstcb = (struct xsctp_tcb *)(buf + *offset); *offset += sizeof(struct xsctp_tcb); } xo_close_list("local-address"); } /* * Print a summary of SCTP connections related to an Internet * protocol. */ void sctp_protopr(u_long off __unused, const char *name __unused, int af1 __unused, int proto) { char *buf; const char *mibvar = "net.inet.sctp.assoclist"; size_t offset = 0; size_t len = 0; struct xsctp_inpcb *xinpcb; if (proto != IPPROTO_SCTP) return; if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { if (errno != ENOENT) xo_warn("sysctl: %s", mibvar); return; } if ((buf = malloc(len)) == NULL) { xo_warnx("malloc %lu bytes", (u_long)len); return; } if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { xo_warn("sysctl: %s", mibvar); free(buf); return; } xinpcb = (struct xsctp_inpcb *)(buf + offset); offset += sizeof(struct xsctp_inpcb); while (xinpcb->last == 0 && offset < len) { sctp_process_inpcb(xinpcb, buf, (const size_t)len, &offset); xinpcb = (struct xsctp_inpcb *)(buf + offset); offset += sizeof(struct xsctp_inpcb); } free(buf); } static void sctp_statesprint(uint32_t state) { int idx; switch (state) { case SCTP_CLOSED: idx = NETSTAT_SCTP_STATES_CLOSED; break; case SCTP_BOUND: idx = NETSTAT_SCTP_STATES_BOUND; break; case SCTP_LISTEN: idx = NETSTAT_SCTP_STATES_LISTEN; break; case SCTP_COOKIE_WAIT: idx = NETSTAT_SCTP_STATES_COOKIE_WAIT; break; case SCTP_COOKIE_ECHOED: idx = NETSTAT_SCTP_STATES_COOKIE_ECHOED; break; case SCTP_ESTABLISHED: idx = NETSTAT_SCTP_STATES_ESTABLISHED; break; case SCTP_SHUTDOWN_SENT: idx = NETSTAT_SCTP_STATES_SHUTDOWN_SENT; break; case SCTP_SHUTDOWN_RECEIVED: idx = NETSTAT_SCTP_STATES_SHUTDOWN_RECEIVED; break; case SCTP_SHUTDOWN_ACK_SENT: idx = NETSTAT_SCTP_STATES_SHUTDOWN_ACK_SENT; break; case SCTP_SHUTDOWN_PENDING: idx = NETSTAT_SCTP_STATES_SHUTDOWN_PENDING; break; default: xo_emit("UNKNOWN {:state/0x%08x}", state); return; } xo_emit("{:state/%s}", sctpstates[idx]); } /* * Dump SCTP statistics structure. */ void sctp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct sctpstat sctpstat; if (fetch_stats("net.inet.sctp.stats", off, &sctpstat, sizeof(sctpstat), kread) != 0) return; xo_open_container(name); xo_emit("{T:/%s}:\n", name); #define p(f, m) if (sctpstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)sctpstat.f, plural(sctpstat.f)) #define p1a(f, m) if (sctpstat.f || sflag <= 1) \ xo_emit(m, (uintmax_t)sctpstat.f) /* * input statistics */ p(sctps_recvpackets, "\t{:received-packets/%ju} " "{N:/input packet%s}\n"); p(sctps_recvdatagrams, "\t\t{:received-datagrams/%ju} " "{N:/datagram%s}\n"); p(sctps_recvpktwithdata, "\t\t{:received-with-data/%ju} " "{N:/packet%s that had data}\n"); p(sctps_recvsacks, "\t\t{:received-sack-chunks/%ju} " "{N:/input SACK chunk%s}\n"); p(sctps_recvdata, "\t\t{:received-data-chunks/%ju} " "{N:/input DATA chunk%s}\n"); p(sctps_recvdupdata, "\t\t{:received-duplicate-data-chunks/%ju} " "{N:/duplicate DATA chunk%s}\n"); p(sctps_recvheartbeat, "\t\t{:received-hb-chunks/%ju} " "{N:/input HB chunk%s}\n"); p(sctps_recvheartbeatack, "\t\t{:received-hb-ack-chunks/%ju} " "{N:/HB-ACK chunk%s}\n"); p(sctps_recvecne, "\t\t{:received-ecne-chunks/%ju} " "{N:/input ECNE chunk%s}\n"); p(sctps_recvauth, "\t\t{:received-auth-chunks/%ju} " "{N:/input AUTH chunk%s}\n"); p(sctps_recvauthmissing, "\t\t{:dropped-missing-auth/%ju} " "{N:/chunk%s missing AUTH}\n"); p(sctps_recvivalhmacid, "\t\t{:dropped-invalid-hmac/%ju} " "{N:/invalid HMAC id%s received}\n"); p(sctps_recvivalkeyid, "\t\t{:dropped-invalid-secret/%ju} " "{N:/invalid secret id%s received}\n"); p1a(sctps_recvauthfailed, "\t\t{:dropped-auth-failed/%ju} " "{N:/auth failed}\n"); p1a(sctps_recvexpress, "\t\t{:received-fast-path/%ju} " "{N:/fast path receives all one chunk}\n"); p1a(sctps_recvexpressm, "\t\t{:receives-fast-path-multipart/%ju} " "{N:/fast path multi-part data}\n"); /* * output statistics */ p(sctps_sendpackets, "\t{:sent-packets/%ju} " "{N:/output packet%s}\n"); p(sctps_sendsacks, "\t\t{:sent-sacks/%ju} " "{N:/output SACK%s}\n"); p(sctps_senddata, "\t\t{:sent-data-chunks/%ju} " "{N:/output DATA chunk%s}\n"); p(sctps_sendretransdata, "\t\t{:sent-retransmitted-data-chunks/%ju} " "{N:/retransmitted DATA chunk%s}\n"); p(sctps_sendfastretrans, "\t\t" "{:sent-fast-retransmitted-data-chunks/%ju} " "{N:/fast retransmitted DATA chunk%s}\n"); p(sctps_sendmultfastretrans, "\t\t" "{:sent-fast-retransmitted-data-chunk-multiple-times/%ju} " "{N:/FR'%s that happened more than once to same chunk}\n"); p(sctps_sendheartbeat, "\t\t{:sent-hb-chunks/%ju} " "{N:/output HB chunk%s}\n"); p(sctps_sendecne, "\t\t{:sent-ecne-chunks/%ju} " "{N:/output ECNE chunk%s}\n"); p(sctps_sendauth, "\t\t{:sent-auth-chunks/%ju} " "{N:/output AUTH chunk%s}\n"); p1a(sctps_senderrors, "\t\t{:send-errors/%ju} " "{N:/ip_output error counter}\n"); /* * PCKDROPREP statistics */ xo_emit("\t{T:Packet drop statistics}:\n"); xo_open_container("drop-statistics"); p1a(sctps_pdrpfmbox, "\t\t{:middle-box/%ju} " "{N:/from middle box}\n"); p1a(sctps_pdrpfehos, "\t\t{:end-host/%ju} " "{N:/from end host}\n"); p1a(sctps_pdrpmbda, "\t\t{:with-data/%ju} " "{N:/with data}\n"); p1a(sctps_pdrpmbct, "\t\t{:non-data/%ju} " "{N:/non-data, non-endhost}\n"); p1a(sctps_pdrpbwrpt, "\t\t{:non-endhost/%ju} " "{N:/non-endhost, bandwidth rep only}\n"); p1a(sctps_pdrpcrupt, "\t\t{:short-header/%ju} " "{N:/not enough for chunk header}\n"); p1a(sctps_pdrpnedat, "\t\t{:short-data/%ju} " "{N:/not enough data to confirm}\n"); p1a(sctps_pdrppdbrk, "\t\t{:chunk-break/%ju} " "{N:/where process_chunk_drop said break}\n"); p1a(sctps_pdrptsnnf, "\t\t{:tsn-not-found/%ju} " "{N:/failed to find TSN}\n"); p1a(sctps_pdrpdnfnd, "\t\t{:reverse-tsn/%ju} " "{N:/attempt reverse TSN lookup}\n"); p1a(sctps_pdrpdiwnp, "\t\t{:confirmed-zero-window/%ju} " "{N:/e-host confirms zero-rwnd}\n"); p1a(sctps_pdrpdizrw, "\t\t{:middle-box-no-space/%ju} " "{N:/midbox confirms no space}\n"); p1a(sctps_pdrpbadd, "\t\t{:bad-data/%ju} " "{N:/data did not match TSN}\n"); p(sctps_pdrpmark, "\t\t{:tsn-marked-fast-retransmission/%ju} " "{N:/TSN'%s marked for Fast Retran}\n"); xo_close_container("drop-statistics"); /* * Timeouts */ xo_emit("\t{T:Timeouts}:\n"); xo_open_container("timeouts"); p(sctps_timoiterator, "\t\t{:iterator/%ju} " "{N:/iterator timer%s fired}\n"); p(sctps_timodata, "\t\t{:t3-data/%ju} " "{N:/T3 data time out%s}\n"); p(sctps_timowindowprobe, "\t\t{:window-probe/%ju} " "{N:/window probe (T3) timer%s fired}\n"); p(sctps_timoinit, "\t\t{:init-timer/%ju} " "{N:/INIT timer%s fired}\n"); p(sctps_timosack, "\t\t{:sack-timer/%ju} " "{N:/sack timer%s fired}\n"); p(sctps_timoshutdown, "\t\t{:shutdown-timer/%ju} " "{N:/shutdown timer%s fired}\n"); p(sctps_timoheartbeat, "\t\t{:heartbeat-timer/%ju} " "{N:/heartbeat timer%s fired}\n"); p1a(sctps_timocookie, "\t\t{:cookie-timer/%ju} " "{N:/a cookie timeout fired}\n"); p1a(sctps_timosecret, "\t\t{:endpoint-changed-cookie/%ju} " "{N:/an endpoint changed its cook}ie" "secret\n"); p(sctps_timopathmtu, "\t\t{:pmtu-timer/%ju} " "{N:/PMTU timer%s fired}\n"); p(sctps_timoshutdownack, "\t\t{:shutdown-timer/%ju} " "{N:/shutdown ack timer%s fired}\n"); p(sctps_timoshutdownguard, "\t\t{:shutdown-guard-timer/%ju} " "{N:/shutdown guard timer%s fired}\n"); p(sctps_timostrmrst, "\t\t{:stream-reset-timer/%ju} " "{N:/stream reset timer%s fired}\n"); p(sctps_timoearlyfr, "\t\t{:early-fast-retransmission-timer/%ju} " "{N:/early FR timer%s fired}\n"); p1a(sctps_timoasconf, "\t\t{:asconf-timer/%ju} " "{N:/an asconf timer fired}\n"); p1a(sctps_timoautoclose, "\t\t{:auto-close-timer/%ju} " "{N:/auto close timer fired}\n"); p(sctps_timoassockill, "\t\t{:asoc-free-timer/%ju} " "{N:/asoc free timer%s expired}\n"); p(sctps_timoinpkill, "\t\t{:input-free-timer/%ju} " "{N:/inp free timer%s expired}\n"); xo_close_container("timeouts"); #if 0 /* * Early fast retransmission counters */ p(sctps_earlyfrstart, "\t%ju TODO:sctps_earlyfrstart\n"); p(sctps_earlyfrstop, "\t%ju TODO:sctps_earlyfrstop\n"); p(sctps_earlyfrmrkretrans, "\t%ju TODO:sctps_earlyfrmrkretrans\n"); p(sctps_earlyfrstpout, "\t%ju TODO:sctps_earlyfrstpout\n"); p(sctps_earlyfrstpidsck1, "\t%ju TODO:sctps_earlyfrstpidsck1\n"); p(sctps_earlyfrstpidsck2, "\t%ju TODO:sctps_earlyfrstpidsck2\n"); p(sctps_earlyfrstpidsck3, "\t%ju TODO:sctps_earlyfrstpidsck3\n"); p(sctps_earlyfrstpidsck4, "\t%ju TODO:sctps_earlyfrstpidsck4\n"); p(sctps_earlyfrstrid, "\t%ju TODO:sctps_earlyfrstrid\n"); p(sctps_earlyfrstrout, "\t%ju TODO:sctps_earlyfrstrout\n"); p(sctps_earlyfrstrtmr, "\t%ju TODO:sctps_earlyfrstrtmr\n"); #endif /* * Others */ p1a(sctps_hdrops, "\t{:dropped-too-short/%ju} " "{N:/packet shorter than header}\n"); p1a(sctps_badsum, "\t{:dropped-bad-checksum/%ju} " "{N:/checksum error}\n"); p1a(sctps_noport, "\t{:dropped-no-endpoint/%ju} " "{N:/no endpoint for port}\n"); p1a(sctps_badvtag, "\t{:dropped-bad-v-tag/%ju} " "{N:/bad v-tag}\n"); p1a(sctps_badsid, "\t{:dropped-bad-sid/%ju} " "{N:/bad SID}\n"); p1a(sctps_nomem, "\t{:dropped-no-memory/%ju} " "{N:/no memory}\n"); p1a(sctps_fastretransinrtt, "\t{:multiple-fast-retransmits-in-rtt/%ju} " "{N:/number of multiple FR in a RT}T window\n"); #if 0 p(sctps_markedretrans, "\t%ju TODO:sctps_markedretrans\n"); #endif p1a(sctps_naglesent, "\t{:rfc813-sent/%ju} " "{N:/RFC813 allowed sending}\n"); p1a(sctps_naglequeued, "\t{:rfc813-queued/%ju} " "{N:/RFC813 does not allow sending}\n"); p1a(sctps_maxburstqueued, "\t{:max-burst-queued/%ju} " "{N:/times max burst prohibited sending}\n"); p1a(sctps_ifnomemqueued, "\t{:no-memory-in-interface/%ju} " "{N:/look ahead tells us no memory in interface}\n"); p(sctps_windowprobed, "\t{:sent-window-probes/%ju} " "{N:/number%s of window probes sent}\n"); p(sctps_lowlevelerr, "\t{:low-level-err/%ju} " "{N:/time%s an output error to clamp down on next user send}\n"); p(sctps_lowlevelerrusr, "\t{:low-level-user-error/%ju} " "{N:/time%s sctp_senderrors were caused from a user}\n"); p(sctps_datadropchklmt, "\t{:dropped-chunk-limit/%ju} " "{N:/number of in data drop%s due to chunk limit reached}\n"); p(sctps_datadroprwnd, "\t{:dropped-rwnd-limit/%ju} " "{N:/number of in data drop%s due to rwnd limit reached}\n"); p(sctps_ecnereducedcwnd, "\t{:ecn-reduced-cwnd/%ju} " "{N:/time%s a ECN reduced the cwnd}\n"); p1a(sctps_vtagexpress, "\t{:v-tag-express-lookup/%ju} " "{N:/used express lookup via vtag}\n"); p1a(sctps_vtagbogus, "\t{:v-tag-collision/%ju} " "{N:/collision in express lookup}\n"); p(sctps_primary_randry, "\t{:sender-ran-dry/%ju} " "{N:/time%s the sender ran dry of user data on primary}\n"); p1a(sctps_cmt_randry, "\t{:cmt-ran-dry/%ju} " "{N:/same for above}\n"); p(sctps_slowpath_sack, "\t{:slow-path-sack/%ju} " "{N:/sack%s the slow way}\n"); p(sctps_wu_sacks_sent, "\t{:sent-window-update-only-sack/%ju} " "{N:/window update only sack%s sent}\n"); p(sctps_sends_with_flags, "\t{:sent-with-sinfo/%ju} " "{N:/send%s with sinfo_flags !=0}\n"); p(sctps_sends_with_unord, "\t{:sent-with-unordered/%ju} " "{N:/unordered send%s}\n"); p(sctps_sends_with_eof, "\t{:sent-with-eof/%ju} " "{N:/send%s with EOF flag set}\n"); p(sctps_sends_with_abort, "\t{:sent-with-abort/%ju} " "{N:/send%s with ABORT flag set}\n"); p(sctps_protocol_drain_calls, "\t{:protocol-drain-called/%ju} " "{N:/time%s protocol drain called}\n"); p(sctps_protocol_drains_done, "\t{:protocol-drain/%ju} " "{N:/time%s we did a protocol drain}\n"); p(sctps_read_peeks, "\t{:read-with-peek/%ju} " "{N:/time%s recv was called with peek}\n"); p(sctps_cached_chk, "\t{:cached-chunks/%ju} " "{N:/cached chunk%s used}\n"); p1a(sctps_cached_strmoq, "\t{:cached-output-queue-used/%ju} " "{N:/cached stream oq's used}\n"); p(sctps_left_abandon, "\t{:messages-abandoned/%ju} " "{N:/unread message%s abandonded by close}\n"); p1a(sctps_send_burst_avoid, "\t{:send-burst-avoidance/%ju} " "{N:/send burst avoidance, already max burst inflight to net}\n"); p1a(sctps_send_cwnd_avoid, "\t{:send-cwnd-avoidance/%ju} " "{N:/send cwnd full avoidance, already max burst inflight " "to net}\n"); p(sctps_fwdtsn_map_over, "\t{:tsn-map-overruns/%ju} " "{N:/number of map array over-run%s via fwd-tsn's}\n"); #undef p #undef p1a xo_close_container(name); } #endif /* SCTP */ diff --git a/usr.bin/netstat/unix.c b/usr.bin/netstat/unix.c index 66bedc2d4294..4d6fd9de8af3 100644 --- a/usr.bin/netstat/unix.c +++ b/usr.bin/netstat/unix.c @@ -1,324 +1,321 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include /* * Display protocol blocks in the unix domain. */ #include #include #include #include #define _WANT_SOCKET #include #include #include #include #define _WANT_UNPCB #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" static void unixdomainpr(struct xunpcb *, struct xsocket *); static const char *const socktype[] = { "#0", "stream", "dgram", "raw", "rdm", "seqpacket" }; static int pcblist_sysctl(int type, char **bufp) { char *buf; size_t len; char mibvar[sizeof "net.local.seqpacket.pcblist"]; snprintf(mibvar, sizeof(mibvar), "net.local.%s.pcblist", socktype[type]); len = 0; if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { if (errno != ENOENT) xo_warn("sysctl: %s", mibvar); return (-1); } if ((buf = malloc(len)) == NULL) { xo_warnx("malloc %lu bytes", (u_long)len); return (-2); } if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { xo_warn("sysctl: %s", mibvar); free(buf); return (-2); } *bufp = buf; return (0); } static int pcblist_kvm(u_long count_off, u_long gencnt_off, u_long head_off, char **bufp) { struct unp_head head; struct unpcb *unp, unp0, unp_conn; u_char sun_len; struct socket so; struct xunpgen xug; struct xunpcb xu; unp_gen_t unp_gencnt; u_int unp_count; char *buf, *p; size_t len; if (count_off == 0 || gencnt_off == 0) return (-2); if (head_off == 0) return (-1); kread(count_off, &unp_count, sizeof(unp_count)); len = 2 * sizeof(xug) + (unp_count + unp_count / 8) * sizeof(xu); if ((buf = malloc(len)) == NULL) { xo_warnx("malloc %lu bytes", (u_long)len); return (-2); } p = buf; #define COPYOUT(obj, size) do { \ if (len < (size)) { \ xo_warnx("buffer size exceeded"); \ goto fail; \ } \ bcopy((obj), p, (size)); \ len -= (size); \ p += (size); \ } while (0) #define KREAD(off, buf, len) do { \ if (kread((uintptr_t)(off), (buf), (len)) != 0) \ goto fail; \ } while (0) /* Write out header. */ kread(gencnt_off, &unp_gencnt, sizeof(unp_gencnt)); xug.xug_len = sizeof xug; xug.xug_count = unp_count; xug.xug_gen = unp_gencnt; xug.xug_sogen = 0; COPYOUT(&xug, sizeof xug); /* Walk the PCB list. */ xu.xu_len = sizeof xu; KREAD(head_off, &head, sizeof(head)); LIST_FOREACH(unp, &head, unp_link) { xu.xu_unpp = (uintptr_t)unp; KREAD(unp, &unp0, sizeof (*unp)); unp = &unp0; if (unp->unp_gencnt > unp_gencnt) continue; if (unp->unp_addr != NULL) { KREAD(unp->unp_addr, &sun_len, sizeof(sun_len)); KREAD(unp->unp_addr, &xu.xu_addr, sun_len); } if (unp->unp_conn != NULL) { KREAD(unp->unp_conn, &unp_conn, sizeof(unp_conn)); if (unp_conn.unp_addr != NULL) { KREAD(unp_conn.unp_addr, &sun_len, sizeof(sun_len)); KREAD(unp_conn.unp_addr, &xu.xu_caddr, sun_len); } } KREAD(unp->unp_socket, &so, sizeof(so)); if (sotoxsocket(&so, &xu.xu_socket) != 0) goto fail; COPYOUT(&xu, sizeof(xu)); } /* Reread the counts and write the footer. */ kread(count_off, &unp_count, sizeof(unp_count)); kread(gencnt_off, &unp_gencnt, sizeof(unp_gencnt)); xug.xug_count = unp_count; xug.xug_gen = unp_gencnt; COPYOUT(&xug, sizeof xug); *bufp = buf; return (0); fail: free(buf); return (-1); #undef COPYOUT #undef KREAD } void unixpr(u_long count_off, u_long gencnt_off, u_long dhead_off, u_long shead_off, u_long sphead_off, bool *first) { char *buf; int ret, type; struct xsocket *so; struct xunpgen *xug, *oxug; struct xunpcb *xunp; u_long head_off; buf = NULL; for (type = SOCK_STREAM; type <= SOCK_SEQPACKET; type++) { if (live) ret = pcblist_sysctl(type, &buf); else { head_off = 0; switch (type) { case SOCK_STREAM: head_off = shead_off; break; case SOCK_DGRAM: head_off = dhead_off; break; case SOCK_SEQPACKET: head_off = sphead_off; break; } ret = pcblist_kvm(count_off, gencnt_off, head_off, &buf); } if (ret == -1) continue; if (ret < 0) return; oxug = xug = (struct xunpgen *)buf; for (xug = (struct xunpgen *)((char *)xug + xug->xug_len); xug->xug_len > sizeof(struct xunpgen); xug = (struct xunpgen *)((char *)xug + xug->xug_len)) { xunp = (struct xunpcb *)xug; so = &xunp->xu_socket; /* Ignore PCBs which were freed during copyout. */ if (xunp->unp_gencnt > oxug->xug_gen) continue; if (*first) { xo_open_list("socket"); *first = false; } xo_open_instance("socket"); unixdomainpr(xunp, so); xo_close_instance("socket"); } if (xug != oxug && xug->xug_gen != oxug->xug_gen) { if (oxug->xug_count > xug->xug_count) { xo_emit("Some {:type/%s} sockets may have " "been {:action/deleted}.\n", socktype[type]); } else if (oxug->xug_count < xug->xug_count) { xo_emit("Some {:type/%s} sockets may have " "been {:action/created}.\n", socktype[type]); } else { xo_emit("Some {:type/%s} sockets may have " "been {:action/created or deleted}", socktype[type]); } } free(buf); } } static void unixdomainpr(struct xunpcb *xunp, struct xsocket *so) { struct sockaddr_un *sa; static int first = 1; char buf1[33]; static const char *titles[2] = { "{T:/%-8.8s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} {T:/%8.8s} " "{T:/%8.8s} {T:/%8.8s} {T:/%8.8s} {T:Addr}\n", "{T:/%-16.16s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} {T:/%16.16s} " "{T:/%16.16s} {T:/%16.16s} {T:/%16.16s} {T:Addr}\n" }; static const char *format[2] = { "{q:address/%8lx} {t:type/%-6.6s} " "{:receive-bytes-waiting/%6u} " "{:send-bytes-waiting/%6u} " "{q:vnode/%8lx} {q:connection/%8lx} " "{q:first-reference/%8lx} {q:next-reference/%8lx}", "{q:address/%16lx} {t:type/%-6.6s} " "{:receive-bytes-waiting/%6u} " "{:send-bytes-waiting/%6u} " "{q:vnode/%16lx} {q:connection/%16lx} " "{q:first-reference/%16lx} {q:next-reference/%16lx}" }; int fmt = (sizeof(void *) == 8) ? 1 : 0; sa = (xunp->xu_addr.sun_family == AF_UNIX) ? &xunp->xu_addr : NULL; if (first && !Lflag) { xo_emit("{T:Active UNIX domain sockets}\n"); xo_emit(titles[fmt], "Address", "Type", "Recv-Q", "Send-Q", "Inode", "Conn", "Refs", "Nextref"); first = 0; } if (Lflag && so->so_qlimit == 0) return; if (Lflag) { snprintf(buf1, sizeof buf1, "%u/%u/%u", so->so_qlen, so->so_incqlen, so->so_qlimit); xo_emit("unix {d:socket/%-32.32s}{e:queue-length/%u}" "{e:incomplete-queue-length/%u}{e:queue-limit/%u}", buf1, so->so_qlen, so->so_incqlen, so->so_qlimit); } else { xo_emit(format[fmt], (long)so->so_pcb, socktype[so->so_type], so->so_rcv.sb_cc, so->so_snd.sb_cc, (long)xunp->unp_vnode, (long)xunp->unp_conn, (long)xunp->xu_firstref, (long)xunp->xu_nextref); } if (sa) xo_emit(" {:path/%.*s}", (int)(sa->sun_len - offsetof(struct sockaddr_un, sun_path)), sa->sun_path); xo_emit("\n"); } diff --git a/usr.bin/newgrp/newgrp.c b/usr.bin/newgrp/newgrp.c index 4a00cbbd45c6..f1da1c8cb1f5 100644 --- a/usr.bin/newgrp/newgrp.c +++ b/usr.bin/newgrp/newgrp.c @@ -1,310 +1,309 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2002 Tim J. Robbins. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * newgrp -- change to a new group */ -#include #include #include #include #include #include #include #include #include #include #include #include #include static void addgroup(const char *grpname); static void doshell(void); static int inarray(gid_t, const gid_t[], int); static void loginshell(void); static void restoregrps(void); static void usage(void); static struct passwd *pwd; static uid_t euid; extern char **environ; /* Manipulate effective user ID. */ #define PRIV_START do { \ if (seteuid(euid) < 0) \ err(1, "seteuid"); \ } while (0) #define PRIV_END do { \ if (seteuid(getuid()) < 0) \ err(1, "seteuid"); \ } while (0) int main(int argc, char *argv[]) { int ch, login; if ((euid = geteuid()) != 0) warnx("need root permissions to function properly, check setuid bit"); if (seteuid(getuid()) < 0) err(1, "seteuid"); if ((pwd = getpwuid(getuid())) == NULL) errx(1, "unknown user"); login = 0; while ((ch = getopt(argc, argv, "-l")) != -1) { switch (ch) { case '-': /* Obsolescent */ case 'l': login = 1; break; default: usage(); } } argc -= optind; argv += optind; switch (argc) { case 0: restoregrps(); break; case 1: addgroup(*argv); break; default: usage(); } if (seteuid(euid) < 0) err(1, "seteuid"); if (setuid(getuid()) < 0) err(1, "setuid"); if (login) loginshell(); else doshell(); /*NOTREACHED*/ exit(1); } static void usage(void) { fprintf(stderr, "usage: newgrp [-l] [group]\n"); exit(1); } static void restoregrps(void) { int initres, setres; PRIV_START; initres = initgroups(pwd->pw_name, pwd->pw_gid); setres = setgid(pwd->pw_gid); PRIV_END; if (initres < 0) warn("initgroups"); if (setres < 0) warn("setgid"); } static void addgroup(const char *grpname) { gid_t *grps; long lgid, ngrps_max; int dbmember, i, ngrps; gid_t egid; struct group *grp; char *ep, *pass, *cryptpw; char **p; egid = getegid(); /* Try it as a group name, then a group id. */ if ((grp = getgrnam(grpname)) == NULL) if ((lgid = strtol(grpname, &ep, 10)) <= 0 || *ep != '\0' || (grp = getgrgid((gid_t)lgid)) == NULL ) { warnx("%s: bad group name", grpname); return; } /* * If the user is not a member of the requested group and the group * has a password, prompt and check it. */ dbmember = 0; if (pwd->pw_gid == grp->gr_gid) dbmember = 1; for (p = grp->gr_mem; *p != NULL; p++) if (strcmp(*p, pwd->pw_name) == 0) { dbmember = 1; break; } if (!dbmember && *grp->gr_passwd != '\0' && getuid() != 0) { pass = getpass("Password:"); if (pass == NULL) return; cryptpw = crypt(pass, grp->gr_passwd); if (cryptpw == NULL || strcmp(grp->gr_passwd, cryptpw) != 0) { fprintf(stderr, "Sorry\n"); return; } } ngrps_max = sysconf(_SC_NGROUPS_MAX) + 1; if ((grps = malloc(sizeof(gid_t) * ngrps_max)) == NULL) err(1, "malloc"); if ((ngrps = getgroups(ngrps_max, (gid_t *)grps)) < 0) { warn("getgroups"); goto end; } /* Remove requested gid from supp. list if it exists. */ if (grp->gr_gid != egid && inarray(grp->gr_gid, grps, ngrps)) { for (i = 0; i < ngrps; i++) if (grps[i] == grp->gr_gid) break; ngrps--; memmove(&grps[i], &grps[i + 1], (ngrps - i) * sizeof(gid_t)); PRIV_START; if (setgroups(ngrps, (const gid_t *)grps) < 0) { PRIV_END; warn("setgroups"); goto end; } PRIV_END; } PRIV_START; if (setgid(grp->gr_gid)) { PRIV_END; warn("setgid"); goto end; } PRIV_END; grps[0] = grp->gr_gid; /* Add old effective gid to supp. list if it does not exist. */ if (egid != grp->gr_gid && !inarray(egid, grps, ngrps)) { if (ngrps == ngrps_max) warnx("too many groups"); else { grps[ngrps++] = egid; PRIV_START; if (setgroups(ngrps, (const gid_t *)grps)) { PRIV_END; warn("setgroups"); goto end; } PRIV_END; } } end: free(grps); } static int inarray(gid_t gid, const gid_t grps[], int ngrps) { int i; for (i = 0; i < ngrps; i++) if (grps[i] == gid) return (1); return (0); } /* * Set the environment to what would be expected if the user logged in * again; this performs the same steps as su(1)'s -l option. */ static void loginshell(void) { char *args[2], **cleanenv, *term, *ticket; const char *shell; login_cap_t *lc; shell = pwd->pw_shell; if (*shell == '\0') shell = _PATH_BSHELL; if (chdir(pwd->pw_dir) < 0) { warn("%s", pwd->pw_dir); chdir("/"); } term = getenv("TERM"); ticket = getenv("KRBTKFILE"); if ((cleanenv = calloc(20, sizeof(char *))) == NULL) err(1, "calloc"); *cleanenv = NULL; environ = cleanenv; lc = login_getpwclass(pwd); setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV); login_close(lc); setenv("USER", pwd->pw_name, 1); setenv("SHELL", shell, 1); setenv("HOME", pwd->pw_dir, 1); if (term != NULL) setenv("TERM", term, 1); if (ticket != NULL) setenv("KRBTKFILE", ticket, 1); if (asprintf(args, "-%s", shell) < 0) err(1, "asprintf"); args[1] = NULL; execv(shell, args); err(1, "%s", shell); } static void doshell(void) { const char *shell; shell = pwd->pw_shell; if (*shell == '\0') shell = _PATH_BSHELL; execl(shell, shell, (char *)NULL); err(1, "%s", shell); } diff --git a/usr.bin/newkey/generic.c b/usr.bin/newkey/generic.c index a2fcaff07e72..faf2247376b0 100644 --- a/usr.bin/newkey/generic.c +++ b/usr.bin/newkey/generic.c @@ -1,127 +1,124 @@ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user or with the express written consent of * Sun Microsystems, Inc. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -#endif - /* * Copyright (C) 1986, Sun Microsystems, Inc. */ #include #include #include #include #include #include #include #include "extern.h" static void adjust(char[HEXKEYBYTES + 1], char *); static void getseed(char *, int, unsigned char *); /* * Generate a seed */ static void getseed(char *seed, int seedsize, unsigned char *pass) { int i; for (i = 0; i < seedsize; i++) { seed[i] = (arc4random() & 0xff) ^ pass[i % 8]; } } /* * Generate a random public/secret key pair */ void genkeys(char *public, char *secret, char *pass) { unsigned int i; # define BASEBITS (8*sizeof (short) - 1) # define BASE (1 << BASEBITS) MINT *pk = mp_itom(0); MINT *sk = mp_itom(0); MINT *tmp; MINT *base = mp_itom((short)BASE); MINT *root = mp_itom(PROOT); MINT *modulus = mp_xtom(HEXMODULUS); short r; unsigned short seed[KEYSIZE/BASEBITS + 1]; char *xkey; getseed((char *)seed, sizeof (seed), (u_char *)pass); for (i = 0; i < KEYSIZE/BASEBITS + 1; i++) { r = seed[i] % BASE; tmp = mp_itom(r); mp_mult(sk, base, sk); mp_madd(sk, tmp, sk); mp_mfree(tmp); } tmp = mp_itom(0); mp_mdiv(sk, modulus, tmp, sk); mp_mfree(tmp); mp_pow(root, sk, modulus, pk); xkey = mp_mtox(sk); adjust(secret, xkey); xkey = mp_mtox(pk); adjust(public, xkey); mp_mfree(sk); mp_mfree(base); mp_mfree(pk); mp_mfree(root); mp_mfree(modulus); } /* * Adjust the input key so that it is 0-filled on the left */ static void adjust(char keyout[HEXKEYBYTES+1], char *keyin) { char *p; char *s; for (p = keyin; *p; p++) ; for (s = keyout + HEXKEYBYTES; p >= keyin; p--, s--) { *s = *p; } while (s >= keyout) { *s-- = '0'; } } diff --git a/usr.bin/newkey/newkey.c b/usr.bin/newkey/newkey.c index e745ff0d9eee..32f449c237b2 100644 --- a/usr.bin/newkey/newkey.c +++ b/usr.bin/newkey/newkey.c @@ -1,230 +1,227 @@ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user or with the express written consent of * Sun Microsystems, Inc. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ -#if !defined(lint) && defined(SCCSIDS) -#endif - /* * Copyright (C) 1986, Sun Microsystems, Inc. */ /* * Administrative tool to add a new user to the publickey database */ -#include + #include #include #include #include #include #ifdef YP #include #include #include #include #endif /* YP */ #include #include #include #include #include #include #include "extern.h" #ifdef YP #define MAXMAPNAMELEN 256 #else #define YPOP_CHANGE 1 /* change, do not add */ #define YPOP_INSERT 2 /* add, do not change */ #define YPOP_DELETE 3 /* delete this entry */ #define YPOP_STORE 4 /* add, or change */ #define ERR_ACCESS 1 #define ERR_MALLOC 2 #define ERR_READ 3 #define ERR_WRITE 4 #define ERR_DBASE 5 #define ERR_KEY 6 #endif #ifdef YP static char YPDBPATH[]="/var/yp"; static char PKMAP[] = "publickey.byname"; #else static char PKFILE[] = "/etc/publickey"; static const char *err_string(int); #endif /* YP */ static void usage(void) __dead2; int main(int argc, char *argv[]) { char name[MAXNETNAMELEN + 1]; char public[HEXKEYBYTES + 1]; char secret[HEXKEYBYTES + 1]; char crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE + 1]; char crypt2[HEXKEYBYTES + KEYCHECKSUMSIZE + 1]; int status; char *pass; struct passwd *pw; #ifdef undef struct hostent *h; #endif if (argc != 3 || !(strcmp(argv[1], "-u") == 0 || strcmp(argv[1], "-h") == 0)) { usage(); } if (geteuid() != 0) errx(1, "must be superuser"); #ifdef YP if (chdir(YPDBPATH) < 0) warn("cannot chdir to %s", YPDBPATH); #endif /* YP */ if (strcmp(argv[1], "-u") == 0) { pw = getpwnam(argv[2]); if (pw == NULL) errx(1, "unknown user: %s", argv[2]); (void)user2netname(name, (int)pw->pw_uid, (char *)NULL); } else { #ifdef undef h = gethostbyname(argv[2]); if (h == NULL) errx(1, "unknown host: %s", argv[1]); (void)host2netname(name, h->h_name, (char *)NULL); #else (void)host2netname(name, argv[2], (char *)NULL); #endif } (void)printf("Adding new key for %s.\n", name); pass = getpass("New password:"); genkeys(public, secret, pass); memcpy(crypt1, secret, HEXKEYBYTES); memcpy(crypt1 + HEXKEYBYTES, secret, KEYCHECKSUMSIZE); crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0; xencrypt(crypt1, pass); memcpy(crypt2, crypt1, HEXKEYBYTES + KEYCHECKSUMSIZE + 1); xdecrypt(crypt2, getpass("Retype password:")); if (memcmp(crypt2, crypt2 + HEXKEYBYTES, KEYCHECKSUMSIZE) != 0 || memcmp(crypt2, secret, HEXKEYBYTES) != 0) errx(1, "password incorrect"); #ifdef YP (void)printf("Please wait for the database to get updated...\n"); #endif if ((status = setpublicmap(name, public, crypt1))) { #ifdef YP errx(1, "unable to update NIS database (%u): %s", status, yperr_string(status)); #else errx(1, "unable to update publickey database (%u): %s", status, err_string(status)); #endif } (void)printf("Your new key has been successfully stored away.\n"); exit(0); /* NOTREACHED */ } static void usage(void) { (void)fprintf(stderr, "%s\n%s\n", "usage: newkey -h hostname", " newkey -u username"); exit(1); } /* * Set the entry in the public key file */ int setpublicmap(char *name, char *public, char *secret) { char pkent[1024]; (void)sprintf(pkent, "%s:%s", public, secret); #ifdef YP return (mapupdate(name, PKMAP, YPOP_STORE, strlen(name), name, strlen(pkent), pkent)); #else return (localupdate(name, PKFILE, YPOP_STORE, strlen(name), name, strlen(pkent), pkent)); #endif } #ifndef YP /* * This returns a pointer to an error message string appropriate * to an input error code. An input value of zero will return * a success message. */ static const char * err_string(int code) { const char *pmesg; switch (code) { case 0: pmesg = "update operation succeeded"; break; case ERR_KEY: pmesg = "no such key in file"; break; case ERR_READ: pmesg = "cannot read the database"; break; case ERR_WRITE: pmesg = "cannot write to the database"; break; case ERR_DBASE: pmesg = "cannot update database"; break; case ERR_ACCESS: pmesg = "permission denied"; break; case ERR_MALLOC: pmesg = "malloc failed"; break; default: pmesg = "unknown error"; break; } return (pmesg); } #endif diff --git a/usr.bin/newkey/update.c b/usr.bin/newkey/update.c index 8e6add14277d..2d2b1b8f8d0e 100644 --- a/usr.bin/newkey/update.c +++ b/usr.bin/newkey/update.c @@ -1,335 +1,332 @@ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user or with the express written consent of * Sun Microsystems, Inc. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ -#ifndef lint -#endif - /* * Copyright (C) 1986, 1989, Sun Microsystems, Inc. */ /* * Administrative tool to add a new user to the publickey database */ -#include + #include #include #include #include #include #ifdef YP #include #include #include #include #endif /* YP */ #include #include #include #include #include #include "extern.h" #ifdef YP static char SHELL[] = "/bin/sh"; static char YPDBPATH[]="/var/yp"; /* This is defined but not used! */ static char UPDATEFILE[] = "updaters"; static int _openchild(char *, FILE **, FILE **); static char *basename(char *path); /* * Determine if requester is allowed to update the given map, * and update it if so. Returns the yp status, which is zero * if there is no access violation. */ int mapupdate(char *requester, char *mapname, u_int op, u_int keylen, char *key, u_int datalen, char *data) { char updater[MAXMAPNAMELEN + 40]; FILE *childargs; FILE *childrslt; #ifdef WEXITSTATUS int status; #else union wait status; #endif pid_t pid; u_int yperrno; #ifdef DEBUG printf("%s %s\n", key, data); #endif (void)sprintf(updater, "make -s -f %s/%s %s", YPDBPATH, /* !!! */ UPDATEFILE, mapname); pid = _openchild(updater, &childargs, &childrslt); if (pid < 0) { return (YPERR_YPERR); } /* * Write to child */ (void)fprintf(childargs, "%s\n", requester); (void)fprintf(childargs, "%u\n", op); (void)fprintf(childargs, "%u\n", keylen); (void)fwrite(key, (int)keylen, 1, childargs); (void)fprintf(childargs, "\n"); (void)fprintf(childargs, "%u\n", datalen); (void)fwrite(data, (int)datalen, 1, childargs); (void)fprintf(childargs, "\n"); (void)fclose(childargs); /* * Read from child */ (void)fscanf(childrslt, "%d", &yperrno); (void)fclose(childrslt); (void)wait(&status); #ifdef WEXITSTATUS if (WEXITSTATUS(status) != 0) { #else if (status.w_retcode != 0) { #endif return (YPERR_YPERR); } return (yperrno); } /* * returns pid, or -1 for failure */ static pid_t _openchild(char *command, FILE **fto, FILE **ffrom) { int i; pid_t pid; int pdto[2]; int pdfrom[2]; char *com; struct rlimit rl; if (pipe(pdto) < 0) { goto error1; } if (pipe(pdfrom) < 0) { goto error2; } switch (pid = fork()) { case -1: goto error3; case 0: /* * child: read from pdto[0], write into pdfrom[1] */ (void)close(0); (void)dup(pdto[0]); (void)close(1); (void)dup(pdfrom[1]); getrlimit(RLIMIT_NOFILE, &rl); for (i = rl.rlim_max - 1; i >= 3; i--) { (void) close(i); } com = malloc((unsigned) strlen(command) + 6); if (com == NULL) { _exit(~0); } (void)sprintf(com, "exec %s", command); execl(SHELL, basename(SHELL), "-c", com, (char *)NULL); _exit(~0); default: /* * parent: write into pdto[1], read from pdfrom[0] */ *fto = fdopen(pdto[1], "w"); (void)close(pdto[0]); *ffrom = fdopen(pdfrom[0], "r"); (void)close(pdfrom[1]); break; } return (pid); /* * error cleanup and return */ error3: (void)close(pdfrom[0]); (void)close(pdfrom[1]); error2: (void)close(pdto[0]); (void)close(pdto[1]); error1: return (-1); } static char * basename(char *path) { char *p; p = strrchr(path, '/'); if (p == NULL) { return (path); } else { return (p + 1); } } #else /* YP */ #define ERR_ACCESS 1 #define ERR_MALLOC 2 #define ERR_READ 3 #define ERR_WRITE 4 #define ERR_DBASE 5 #define ERR_KEY 6 static int match(char *, char *); /* * Determine if requester is allowed to update the given map, * and update it if so. Returns the status, which is zero * if there is no access violation. This function updates * the local file and then shuts up. */ int localupdate(char *name, char *filename, u_int op, u_int keylen __unused, char *key, u_int datalen __unused, char *data) { char line[256]; FILE *rf; FILE *wf; char *tmpname; int err; /* * Check permission */ if (strcmp(name, key) != 0) { return (ERR_ACCESS); } if (strcmp(name, "nobody") == 0) { /* * Can't change "nobody"s key. */ return (ERR_ACCESS); } /* * Open files */ tmpname = malloc(strlen(filename) + 4); if (tmpname == NULL) { return (ERR_MALLOC); } sprintf(tmpname, "%s.tmp", filename); rf = fopen(filename, "r"); if (rf == NULL) { err = ERR_READ; goto cleanup; } wf = fopen(tmpname, "w"); if (wf == NULL) { fclose(rf); err = ERR_WRITE; goto cleanup; } err = -1; while (fgets(line, sizeof (line), rf)) { if (err < 0 && match(line, name)) { switch (op) { case YPOP_INSERT: err = ERR_KEY; break; case YPOP_STORE: case YPOP_CHANGE: fprintf(wf, "%s %s\n", key, data); err = 0; break; case YPOP_DELETE: /* do nothing */ err = 0; break; } } else { fputs(line, wf); } } if (err < 0) { switch (op) { case YPOP_CHANGE: case YPOP_DELETE: err = ERR_KEY; break; case YPOP_INSERT: case YPOP_STORE: err = 0; fprintf(wf, "%s %s\n", key, data); break; } } fclose(wf); fclose(rf); if (err == 0) { if (rename(tmpname, filename) < 0) { err = ERR_DBASE; goto cleanup; } } else { if (unlink(tmpname) < 0) { err = ERR_DBASE; goto cleanup; } } cleanup: free(tmpname); return (err); } static int match(char *line, char *name) { int len; len = strlen(name); return (strncmp(line, name, len) == 0 && (line[len] == ' ' || line[len] == '\t')); } #endif /* !YP */ diff --git a/usr.bin/passwd/passwd.c b/usr.bin/passwd/passwd.c index b2ca1b352108..f1a25c1f21cb 100644 --- a/usr.bin/passwd/passwd.c +++ b/usr.bin/passwd/passwd.c @@ -1,164 +1,163 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2002 Networks Associates Technologies, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by ThinkSec AS and * NAI Labs, the Security Research Division of Network Associates, Inc. * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the * DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include static pam_handle_t *pamh; static struct pam_conv pamc = { openpam_ttyconv, NULL }; static char *yp_domain; static char *yp_host; static void usage(void) { fprintf(stderr, "usage: passwd [-ly] [-d domain] [-h host] [user]\n"); exit(1); } int main(int argc, char *argv[]) { char hostname[MAXHOSTNAMELEN]; struct passwd *pwd = NULL; /* Keep compiler happy. */ int o, pam_err; uid_t uid; while ((o = getopt(argc, argv, "d:h:loy")) != -1) switch (o) { case 'd': yp_domain = optarg; break; case 'h': yp_host = optarg; break; case 'l': case 'o': case 'y': /* compatibility */ break; default: usage(); } argc -= optind; argv += optind; uid = getuid(); switch (argc) { case 0: if ((pwd = getpwuid(uid)) == NULL) errx(1, "who are you?"); break; case 1: if ((pwd = getpwnam(*argv)) == NULL) errx(1, "%s: no such user", *argv); break; default: usage(); } if (uid != 0 && uid != pwd->pw_uid) errx(1, "permission denied"); /* check where the user's from */ switch (pwd->pw_fields & _PWF_SOURCE) { case _PWF_FILES: fprintf(stderr, "Changing local password for %s\n", pwd->pw_name); break; case _PWF_NIS: fprintf(stderr, "Changing NIS password for %s\n", pwd->pw_name); break; default: /* XXX: Green men ought to be supported via PAM. */ errx(1, "Sorry, `passwd' can only change passwords for local or NIS users."); } #define pam_check(func) do { \ if (pam_err != PAM_SUCCESS) { \ if (pam_err == PAM_AUTH_ERR || pam_err == PAM_PERM_DENIED || \ pam_err == PAM_AUTHTOK_RECOVERY_ERR) \ warnx("sorry"); \ else \ warnx("%s(): %s", func, pam_strerror(pamh, pam_err)); \ goto end; \ } \ } while (0) /* initialize PAM */ pam_err = pam_start("passwd", pwd->pw_name, &pamc, &pamh); pam_check("pam_start"); pam_err = pam_set_item(pamh, PAM_TTY, ttyname(STDERR_FILENO)); pam_check("pam_set_item"); gethostname(hostname, sizeof hostname); pam_err = pam_set_item(pamh, PAM_RHOST, hostname); pam_check("pam_set_item"); pam_err = pam_set_item(pamh, PAM_RUSER, getlogin()); pam_check("pam_set_item"); /* set YP domain and host */ pam_err = pam_set_data(pamh, "yp_domain", yp_domain, NULL); pam_check("pam_set_data"); pam_err = pam_set_data(pamh, "yp_server", yp_host, NULL); pam_check("pam_set_data"); /* set new password */ pam_err = pam_chauthtok(pamh, 0); pam_check("pam_chauthtok"); end: pam_end(pamh, pam_err); exit(pam_err == PAM_SUCCESS ? 0 : 1); } diff --git a/usr.bin/pathchk/pathchk.c b/usr.bin/pathchk/pathchk.c index 821c5de0ee22..4b9dddf0630b 100644 --- a/usr.bin/pathchk/pathchk.c +++ b/usr.bin/pathchk/pathchk.c @@ -1,202 +1,201 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2002 Tim J. Robbins. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * pathchk -- check pathnames * * Check whether files could be created with the names specified on the * command line. If -p is specified, check whether the pathname is portable * to all POSIX systems. */ -#include #include #include #include #include #include #include #include #include #include static int check(const char *); static int portable(const char *); static void usage(void); static int pflag; /* Perform portability checks */ static int Pflag; /* Check for empty paths, leading '-' */ int main(int argc, char *argv[]) { int ch, rval; const char *arg; while ((ch = getopt(argc, argv, "pP")) > 0) { switch (ch) { case 'p': pflag = 1; break; case 'P': Pflag = 1; break; default: usage(); /*NOTREACHED*/ } } argc -= optind; argv += optind; if (argc == 0) usage(); rval = 0; while ((arg = *argv++) != NULL) rval |= check(arg); exit(rval); } static void usage(void) { fprintf(stderr, "usage: pathchk [-Pp] pathname ...\n"); exit(1); } static int check(const char *path) { struct stat sb; long complen, namemax, pathmax, svnamemax; int last; char *end, *p, *pathd; if ((pathd = strdup(path)) == NULL) err(1, "strdup"); p = pathd; if (Pflag && *p == '\0') { warnx("%s: empty pathname", path); goto bad; } if ((Pflag || pflag) && (*p == '-' || strstr(p, "/-") != NULL)) { warnx("%s: contains a component starting with '-'", path); goto bad; } if (!pflag) { errno = 0; namemax = pathconf(*p == '/' ? "/" : ".", _PC_NAME_MAX); if (namemax == -1 && errno != 0) namemax = NAME_MAX; } else namemax = _POSIX_NAME_MAX; for (;;) { p += strspn(p, "/"); complen = (long)strcspn(p, "/"); end = p + complen; last = *end == '\0'; *end = '\0'; if (namemax != -1 && complen > namemax) { warnx("%s: %s: component too long (limit %ld)", path, p, namemax); goto bad; } if (!pflag && stat(pathd, &sb) == -1 && errno != ENOENT) { warn("%s: %.*s", path, (int)(strlen(pathd) - complen - 1), pathd); goto bad; } if (pflag && !portable(p)) { warnx("%s: %s: component contains non-portable " "character", path, p); goto bad; } if (last) break; if (!pflag) { errno = 0; svnamemax = namemax; namemax = pathconf(pathd, _PC_NAME_MAX); if (namemax == -1 && errno != 0) namemax = svnamemax; } *end = '/'; p = end + 1; } if (!pflag) { errno = 0; pathmax = pathconf(path, _PC_PATH_MAX); if (pathmax == -1 && errno != 0) pathmax = PATH_MAX; } else pathmax = _POSIX_PATH_MAX; if (pathmax != -1 && strlen(path) >= (size_t)pathmax) { warnx("%s: path too long (limit %ld)", path, pathmax - 1); goto bad; } free(pathd); return (0); bad: free(pathd); return (1); } /* * Check whether a path component contains only portable characters. */ static int portable(const char *path) { static const char charset[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789._-"; long s; s = strspn(path, charset); if (path[s] != '\0') return (0); return (1); } diff --git a/usr.bin/posixshmcontrol/posixshmcontrol.c b/usr.bin/posixshmcontrol/posixshmcontrol.c index 8b9206c56800..fde03e495d07 100644 --- a/usr.bin/posixshmcontrol/posixshmcontrol.c +++ b/usr.bin/posixshmcontrol/posixshmcontrol.c @@ -1,593 +1,592 @@ /*- * Copyright (c) 2019 The FreeBSD Foundation * * This software was developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void usage(void) { fprintf(stderr, "Usage:\n" "posixshmcontrol create [-m ] [-l ] ...\n" "posixshmcontrol rm ...\n" "posixshmcontrol ls [-h] [-n] [-j jail]\n" "posixshmcontrol dump ...\n" "posixshmcontrol stat [-h] [-n] ...\n" "posixshmcontrol truncate [-s ] ...\n"); } static int create_one_shm(const char *path, long mode, int idx) { int fd; if (idx == -1) { fd = shm_open(path, O_RDWR | O_CREAT, mode); if (fd == -1) { warn("create %s", path); return (1); } } else { fd = shm_create_largepage(path, O_RDWR, idx, SHM_LARGEPAGE_ALLOC_DEFAULT, mode); if (fd == -1) { warn("shm_create_largepage %s psind %d", path, idx); return (1); } } close(fd); return (0); } static int create_shm(int argc, char **argv) { char *end; size_t *pagesizes; long mode; uint64_t pgsz; int c, i, idx, pn, ret, ret1; bool printed; mode = 0600; idx = -1; while ((c = getopt(argc, argv, "l:m:")) != -1) { switch (c) { case 'm': errno = 0; mode = strtol(optarg, &end, 0); if (mode == 0 && errno != 0) err(1, "mode"); if (*end != '\0') errx(1, "non-integer mode"); break; case 'l': if (expand_number(optarg, &pgsz) == -1) err(1, "size"); pn = getpagesizes(NULL, 0); if (pn == -1) err(1, "getpagesizes"); pagesizes = malloc(sizeof(size_t) * pn); if (pagesizes == NULL) err(1, "malloc"); if (getpagesizes(pagesizes, pn) == -1) err(1, "gtpagesizes"); for (idx = 0; idx < pn; idx++) { if (pagesizes[idx] == pgsz) break; } if (idx == pn) { fprintf(stderr, "pagesize should be superpagesize, supported sizes:"); printed = false; for (i = 0; i < pn; i++) { if (pagesizes[i] == 0 || pagesizes[i] == (size_t) getpagesize()) continue; printed = true; fprintf(stderr, " %zu", pagesizes[i]); } if (!printed) fprintf(stderr, " none"); fprintf(stderr, "\n"); exit(1); } if (pgsz == (uint64_t)getpagesize()) errx(1, "pagesize should be large"); free(pagesizes); break; case '?': default: usage(); return (2); } } argc -= optind; argv += optind; if (argc == 0) { usage(); return (2); } ret = 0; for (i = 0; i < argc; i++) { ret1 = create_one_shm(argv[i], mode, idx); if (ret1 != 0 && ret == 0) ret = ret1; } return (ret); } static int delete_one_shm(const char *path) { int error, ret; error = shm_unlink(path); if (error != 0) { warn("unlink of %s failed", path); ret = 1; } else { ret = 0; } return (ret); } static int delete_shm(int argc, char **argv) { int i, ret, ret1; if (argc == 1) { usage(); return (2); } ret = 0; for (i = 1; i < argc; i++) { ret1 = delete_one_shm(argv[i]); if (ret1 != 0 && ret == 0) ret = ret1; } return (ret); } static const char listmib[] = "kern.ipc.posix_shm_list"; static void shm_decode_mode(mode_t m, char *str) { int i; i = 0; str[i++] = (m & S_IRUSR) != 0 ? 'r' : '-'; str[i++] = (m & S_IWUSR) != 0 ? 'w' : '-'; str[i++] = (m & S_IXUSR) != 0 ? 'x' : '-'; str[i++] = (m & S_IRGRP) != 0 ? 'r' : '-'; str[i++] = (m & S_IWGRP) != 0 ? 'w' : '-'; str[i++] = (m & S_IXGRP) != 0 ? 'x' : '-'; str[i++] = (m & S_IROTH) != 0 ? 'r' : '-'; str[i++] = (m & S_IWOTH) != 0 ? 'w' : '-'; str[i++] = (m & S_IXOTH) != 0 ? 'x' : '-'; str[i] = '\0'; } static int list_shm(int argc, char **argv) { char *buf, *bp, *ep, jailpath[MAXPATHLEN], sizebuf[8], str[10]; const char *jailparam; const struct kinfo_file *kif; struct stat st; int c, error, fd, jid, mib[3], ret; size_t len, jailpathlen, miblen; bool hsize, jailed, uname; hsize = false; jailed = false; uname = true; while ((c = getopt(argc, argv, "hj:n")) != -1) { switch (c) { case 'h': hsize = true; break; case 'n': uname = false; break; case 'j': jid = strtoul(optarg, &ep, 10); if (ep > optarg && !*ep) { jailparam = "jid"; jailed = jid > 0; } else { jailparam = "name"; jailed = true; } if (jailed) { if (jail_getv(0, jailparam, optarg, "path", jailpath, NULL) < 0) { if (errno == ENOENT) warnx("no such jail: %s", optarg); else warnx("%s", jail_errmsg); return (1); } jailpathlen = strlen(jailpath); jailpath[jailpathlen] = '/'; } break; default: usage(); return (2); } } if (argc != optind) { usage(); return (2); } miblen = nitems(mib); error = sysctlnametomib(listmib, mib, &miblen); if (error == -1) { warn("cannot translate %s", listmib); return (1); } len = 0; error = sysctl(mib, miblen, NULL, &len, NULL, 0); if (error == -1) { warn("cannot get %s length", listmib); return (1); } len = len * 4 / 3; buf = malloc(len); if (buf == NULL) { warn("malloc"); return (1); } error = sysctl(mib, miblen, buf, &len, NULL, 0); if (error != 0) { warn("reading %s", listmib); ret = 1; goto out; } ret = 0; printf("MODE \tOWNER\tGROUP\tSIZE\tPATH\n"); for (bp = buf; bp < buf + len; bp += kif->kf_structsize) { kif = (const struct kinfo_file *)(void *)bp; if (kif->kf_structsize == 0) break; if (jailed && strncmp(kif->kf_path, jailpath, jailpathlen + 1)) continue; fd = shm_open(kif->kf_path, O_RDONLY, 0); if (fd == -1) { if (errno != EACCES) { warn("open %s", kif->kf_path); ret = 1; } continue; } error = fstat(fd, &st); close(fd); if (error != 0) { warn("stat %s", kif->kf_path); ret = 1; continue; } shm_decode_mode(kif->kf_un.kf_file.kf_file_mode, str); printf("%s\t", str); if (uname) { printf("%s\t%s\t", user_from_uid(st.st_uid, 0), group_from_gid(st.st_gid, 0)); } else { printf("%d\t%d\t", st.st_uid, st.st_gid); } if (hsize) { humanize_number(sizebuf, sizeof(sizebuf), kif->kf_un.kf_file.kf_file_size, "", HN_AUTOSCALE, HN_NOSPACE); printf("%s\t", sizebuf); } else { printf("%jd\t", (uintmax_t)kif->kf_un.kf_file.kf_file_size); } printf("%s\n", kif->kf_path); } out: free(buf); return (ret); } static int read_one_shm(const char *path) { char buf[4096]; ssize_t size, se; int fd, ret; ret = 1; fd = shm_open(path, O_RDONLY, 0); if (fd == -1) { warn("open %s", path); goto out; } for (;;) { size = read(fd, buf, sizeof(buf)); if (size > 0) { se = fwrite(buf, 1, size, stdout); if (se < size) { warnx("short write to stdout"); goto out; } } if (size == (ssize_t)sizeof(buf)) continue; if (size >= 0 && size < (ssize_t)sizeof(buf)) { ret = 0; goto out; } warn("read from %s", path); goto out; } out: close(fd); return (ret); } static int read_shm(int argc, char **argv) { int i, ret, ret1; if (argc == 1) { usage(); return (2); } ret = 0; for (i = 1; i < argc; i++) { ret1 = read_one_shm(argv[i]); if (ret1 != 0 && ret == 0) ret = ret1; } return (ret); } static int stat_one_shm(const char *path, bool hsize, bool uname) { char sizebuf[8]; struct stat st; int error, fd, ret; struct shm_largepage_conf conf_dummy; bool largepage; fd = shm_open(path, O_RDONLY, 0); if (fd == -1) { warn("open %s", path); return (1); } ret = 0; error = fstat(fd, &st); if (error == -1) { warn("stat %s", path); ret = 1; } else { printf("path\t%s\n", path); printf("inode\t%jd\n", (uintmax_t)st.st_ino); printf("mode\t%#o\n", st.st_mode); printf("nlink\t%jd\n", (uintmax_t)st.st_nlink); if (uname) { printf("owner\t%s\n", user_from_uid(st.st_uid, 0)); printf("group\t%s\n", group_from_gid(st.st_gid, 0)); } else { printf("uid\t%d\n", st.st_uid); printf("gid\t%d\n", st.st_gid); } if (hsize) { humanize_number(sizebuf, sizeof(sizebuf), st.st_size, "", HN_AUTOSCALE, HN_NOSPACE); printf("size\t%s\n", sizebuf); } else { printf("size\t%jd\n", (uintmax_t)st.st_size); } printf("atime\t%ld.%09ld\n", (long)st.st_atime, (long)st.st_atim.tv_nsec); printf("mtime\t%ld.%09ld\n", (long)st.st_mtime, (long)st.st_mtim.tv_nsec); printf("ctime\t%ld.%09ld\n", (long)st.st_ctime, (long)st.st_ctim.tv_nsec); printf("birth\t%ld.%09ld\n", (long)st.st_birthtim.tv_sec, (long)st.st_birthtim.tv_nsec); error = ioctl(fd, FIOGSHMLPGCNF, &conf_dummy); largepage = error == 0; if (st.st_blocks != 0 && largepage) printf("pagesz\t%jd\n", roundup((uintmax_t)st.st_size, PAGE_SIZE) / st.st_blocks); else printf("pages\t%jd\n", st.st_blocks); } close(fd); return (ret); } static int stat_shm(int argc, char **argv) { int c, i, ret, ret1; bool hsize, uname; hsize = false; uname = true; while ((c = getopt(argc, argv, "hn")) != -1) { switch (c) { case 'h': hsize = true; break; case 'n': uname = false; break; default: usage(); return (2); } } argc -= optind; argv += optind; if (argc == 0) { usage(); return (2); } ret = 0; for (i = 0; i < argc; i++) { ret1 = stat_one_shm(argv[i], hsize, uname); if (ret1 != 0 && ret == 0) ret = ret1; } return (ret); } static int truncate_one_shm(const char *path, uint64_t newsize) { int error, fd, ret; ret = 0; fd = shm_open(path, O_RDWR, 0); if (fd == -1) { warn("open %s", path); return (1); } error = ftruncate(fd, newsize); if (error == -1) { warn("truncate %s", path); ret = 1; } close(fd); return (ret); } static int truncate_shm(int argc, char **argv) { uint64_t newsize; int c, i, ret, ret1; newsize = 0; while ((c = getopt(argc, argv, "s:")) != -1) { switch (c) { case 's': if (expand_number(optarg, &newsize) == -1) err(1, "size"); break; case '?': default: return (2); } } argc -= optind; argv += optind; if (argc == 0) { usage(); return (2); } ret = 0; for (i = 0; i < argc; i++) { ret1 = truncate_one_shm(argv[i], newsize); if (ret1 != 0 && ret == 0) ret = ret1; } return (ret); } struct opmode { const char *cmd; int (*impl)(int argc, char **argv); }; static const struct opmode opmodes[] = { { .cmd = "create", .impl = create_shm}, { .cmd = "rm", .impl = delete_shm, }, { .cmd = "list", .impl = list_shm }, { .cmd = "ls", .impl = list_shm }, { .cmd = "dump", .impl = read_shm, }, { .cmd = "stat", .impl = stat_shm, }, { .cmd = "truncate", .impl = truncate_shm, }, }; int main(int argc, char *argv[]) { const struct opmode *opmode; int i, ret; ret = 0; opmode = NULL; if (argc < 2) { usage(); exit(2); } for (i = 0; i < (int)nitems(opmodes); i++) { if (strcmp(argv[1], opmodes[i].cmd) == 0) { opmode = &opmodes[i]; break; } } if (opmode == NULL) { usage(); exit(2); } ret = opmode->impl(argc - 1, argv + 1); exit(ret); } diff --git a/usr.bin/pr/egetopt.c b/usr.bin/pr/egetopt.c index 215507b9b20b..cf101bf03020 100644 --- a/usr.bin/pr/egetopt.c +++ b/usr.bin/pr/egetopt.c @@ -1,214 +1,211 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1991 Keith Muller. * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * 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 -#endif - #include #include #include #include #include "extern.h" /* * egetopt: get option letter from argument vector (an extended * version of getopt). * * Non standard additions to the ostr specs are: * 1) '?': immediate value following arg is optional (no white space * between the arg and the value) * 2) '#': +/- followed by a number (with an optional sign but * no white space between the arg and the number). The - may be * combined with other options, but the + cannot. */ int eopterr = 1; /* if error message should be printed */ int eoptind = 1; /* index into parent argv vector */ int eoptopt; /* character checked for validity */ char *eoptarg; /* argument associated with option */ #define BADCH (int)'?' static char emsg[] = ""; int egetopt(int nargc, char * const *nargv, const char *ostr) { static char *place = emsg; /* option letter processing */ char *oli; /* option letter list index */ static int delim; /* which option delimiter */ char *p; static char savec = '\0'; if (savec != '\0') { *place = savec; savec = '\0'; } if (!*place) { /* * update scanning pointer */ if ((eoptind >= nargc) || ((*(place = nargv[eoptind]) != '-') && (*place != '+'))) { place = emsg; return (-1); } delim = (int)*place; if (place[1] && *++place == '-' && !place[1]) { /* * found "--" */ ++eoptind; place = emsg; return (-1); } } /* * check option letter */ if ((eoptopt = (int)*place++) == (int)':' || (eoptopt == (int)'?') || !(oli = strchr(ostr, eoptopt))) { /* * if the user didn't specify '-' as an option, * assume it means -1 when by itself. */ if ((eoptopt == (int)'-') && !*place) return (-1); if (strchr(ostr, '#') && (isdigit(eoptopt) || (((eoptopt == (int)'-') || (eoptopt == (int)'+')) && isdigit(*place)))) { /* * # option: +/- with a number is ok */ for (p = place; *p != '\0'; ++p) { if (!isdigit(*p)) break; } eoptarg = place-1; if (*p == '\0') { place = emsg; ++eoptind; } else { place = p; savec = *p; *place = '\0'; } return (delim); } if (!*place) ++eoptind; if (eopterr) { if (!(p = strrchr(*nargv, '/'))) p = *nargv; else ++p; (void)fprintf(stderr, "%s: illegal option -- %c\n", p, eoptopt); } return (BADCH); } if (delim == (int)'+') { /* * '+' is only allowed with numbers */ if (!*place) ++eoptind; if (eopterr) { if (!(p = strrchr(*nargv, '/'))) p = *nargv; else ++p; (void)fprintf(stderr, "%s: illegal '+' delimiter with option -- %c\n", p, eoptopt); } return (BADCH); } ++oli; if ((*oli != ':') && (*oli != '?')) { /* * don't need argument */ eoptarg = NULL; if (!*place) ++eoptind; return (eoptopt); } if (*place) { /* * no white space */ eoptarg = place; } else if (*oli == '?') { /* * no arg, but NOT required */ eoptarg = NULL; } else if (nargc <= ++eoptind) { /* * no arg, but IS required */ place = emsg; if (eopterr) { if (!(p = strrchr(*nargv, '/'))) p = *nargv; else ++p; (void)fprintf(stderr, "%s: option requires an argument -- %c\n", p, eoptopt); } return (BADCH); } else { /* * arg has white space */ eoptarg = nargv[eoptind]; } place = emsg; ++eoptind; return (eoptopt); } diff --git a/usr.bin/primes/pattern.c b/usr.bin/primes/pattern.c index 74a680282219..2ac84be69979 100644 --- a/usr.bin/primes/pattern.c +++ b/usr.bin/primes/pattern.c @@ -1,439 +1,436 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Landon Curt Noll. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ - /* * pattern - the Eratosthenes sieve on odd numbers for 3,5,7,11 and 13 * * By: Landon Curt Noll chongo@toad.com * * chongo /\oo/\ * * To avoid excessive sieves for small factors, we use the table below to * setup our sieve blocks. Each element represents an odd number starting * with 1. All non-zero elements are factors of 3, 5, 7, 11 and 13. */ #include #include "primes.h" const char pattern[] = { 1,0,0,0,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 0,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,1,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,0,0,0, 0,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,0,1,0,0,1,0,1, 0,0,0,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,0,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0,0,0,1, 1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,0,0,0,1,0,1, 1,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,0, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 0,0,0,1,0,0,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,0,1, 1,0,0,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,0,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,0,0,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,0,0,0,0,1,0,0, 1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1, 0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,0,0,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0, 0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1, 1,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1, 1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,0,0,1, 0,0,0,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 0,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,0, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,0,0,0,0,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,0,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 1,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 0,0,0,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1, 0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0, 1,0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,0,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,0,0,0, 1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,1, 0,0,0,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,0,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,0,0,1, 0,0,1,0,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0, 1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,0,0,0, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,1, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,0,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,0,0,0,0,0,0,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 0,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1, 0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,0, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,0,0,0, 1,0,0,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1, 1,0,1,0,0,1,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1, 1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0, 0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,0,0,0,0,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,0,0,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0, 1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,1,1,0,0,0,0,0,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1, 0,0,1,0,0,0,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,0,0,0,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,0,0,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 0,0,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,0,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,0,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,1, 1,0,1,0,0,0,0,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0, 1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,0,0,1, 1,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,0,0, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1, 0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1, 0,0,0,1,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,1, 1,0,1,0,0,0,0,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1, 1,0,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,0,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,0,1,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,0,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 1,0,0,0,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0, 0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0, 1,0,0,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0,0, 0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1, 0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,0,0,0, 1,0,1,0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,0, 0,0,0,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,0,0,1,0,0,0, 0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,1, 0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,0, 1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0, 1,0,0,1,0,1,0,0,1,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,0,1, 1,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,0,0,0,0,0,1,1,0,0, 1,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,1,0,0,1, 0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1, 1,0,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,1, 0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,1, 1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1, 0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0,1, 1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,0, 1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1, 1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,1,0,0, 0,0,1,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1, 0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,0,0,0, 1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1, 1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1, 1,0,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0,0,0,1,1,0,0, 1,0,1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,1,0,0,1,0,1,0,0,1, 0,0,1,1,0,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1, 0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,0,0,0,1 }; const size_t pattern_size = (sizeof(pattern)/sizeof(pattern[0])); diff --git a/usr.bin/primes/pr_tbl.c b/usr.bin/primes/pr_tbl.c index 986c3e272c9e..70615b35dfcf 100644 --- a/usr.bin/primes/pr_tbl.c +++ b/usr.bin/primes/pr_tbl.c @@ -1,543 +1,540 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Landon Curt Noll. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef lint -#endif /* not lint */ - /* * prime - prime table * * By: Landon Curt Noll chongo@toad.com, ...!{sun,tolsoft}!hoptoad!chongo * * chongo /\oo/\ * * We are able to sieve 2^32-1 because this table has primes up to 65537 * and 65537^2 > 2^32-1. */ #include #include "primes.h" const ubig prime[] = { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103, 107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199, 211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313, 317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433, 439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563, 569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673, 677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811, 821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941, 947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051, 1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163, 1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279, 1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399, 1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489, 1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597,1601, 1607,1609,1613,1619,1621,1627,1637,1657,1663,1667,1669,1693,1697,1699,1709, 1721,1723,1733,1741,1747,1753,1759,1777,1783,1787,1789,1801,1811,1823,1831, 1847,1861,1867,1871,1873,1877,1879,1889,1901,1907,1913,1931,1933,1949,1951, 1973,1979,1987,1993,1997,1999,2003,2011,2017,2027,2029,2039,2053,2063,2069, 2081,2083,2087,2089,2099,2111,2113,2129,2131,2137,2141,2143,2153,2161,2179, 2203,2207,2213,2221,2237,2239,2243,2251,2267,2269,2273,2281,2287,2293,2297, 2309,2311,2333,2339,2341,2347,2351,2357,2371,2377,2381,2383,2389,2393,2399, 2411,2417,2423,2437,2441,2447,2459,2467,2473,2477,2503,2521,2531,2539,2543, 2549,2551,2557,2579,2591,2593,2609,2617,2621,2633,2647,2657,2659,2663,2671, 2677,2683,2687,2689,2693,2699,2707,2711,2713,2719,2729,2731,2741,2749,2753, 2767,2777,2789,2791,2797,2801,2803,2819,2833,2837,2843,2851,2857,2861,2879, 2887,2897,2903,2909,2917,2927,2939,2953,2957,2963,2969,2971,2999,3001,3011, 3019,3023,3037,3041,3049,3061,3067,3079,3083,3089,3109,3119,3121,3137,3163, 3167,3169,3181,3187,3191,3203,3209,3217,3221,3229,3251,3253,3257,3259,3271, 3299,3301,3307,3313,3319,3323,3329,3331,3343,3347,3359,3361,3371,3373,3389, 3391,3407,3413,3433,3449,3457,3461,3463,3467,3469,3491,3499,3511,3517,3527, 3529,3533,3539,3541,3547,3557,3559,3571,3581,3583,3593,3607,3613,3617,3623, 3631,3637,3643,3659,3671,3673,3677,3691,3697,3701,3709,3719,3727,3733,3739, 3761,3767,3769,3779,3793,3797,3803,3821,3823,3833,3847,3851,3853,3863,3877, 3881,3889,3907,3911,3917,3919,3923,3929,3931,3943,3947,3967,3989,4001,4003, 4007,4013,4019,4021,4027,4049,4051,4057,4073,4079,4091,4093,4099,4111,4127, 4129,4133,4139,4153,4157,4159,4177,4201,4211,4217,4219,4229,4231,4241,4243, 4253,4259,4261,4271,4273,4283,4289,4297,4327,4337,4339,4349,4357,4363,4373, 4391,4397,4409,4421,4423,4441,4447,4451,4457,4463,4481,4483,4493,4507,4513, 4517,4519,4523,4547,4549,4561,4567,4583,4591,4597,4603,4621,4637,4639,4643, 4649,4651,4657,4663,4673,4679,4691,4703,4721,4723,4729,4733,4751,4759,4783, 4787,4789,4793,4799,4801,4813,4817,4831,4861,4871,4877,4889,4903,4909,4919, 4931,4933,4937,4943,4951,4957,4967,4969,4973,4987,4993,4999,5003,5009,5011, 5021,5023,5039,5051,5059,5077,5081,5087,5099,5101,5107,5113,5119,5147,5153, 5167,5171,5179,5189,5197,5209,5227,5231,5233,5237,5261,5273,5279,5281,5297, 5303,5309,5323,5333,5347,5351,5381,5387,5393,5399,5407,5413,5417,5419,5431, 5437,5441,5443,5449,5471,5477,5479,5483,5501,5503,5507,5519,5521,5527,5531, 5557,5563,5569,5573,5581,5591,5623,5639,5641,5647,5651,5653,5657,5659,5669, 5683,5689,5693,5701,5711,5717,5737,5741,5743,5749,5779,5783,5791,5801,5807, 5813,5821,5827,5839,5843,5849,5851,5857,5861,5867,5869,5879,5881,5897,5903, 5923,5927,5939,5953,5981,5987,6007,6011,6029,6037,6043,6047,6053,6067,6073, 6079,6089,6091,6101,6113,6121,6131,6133,6143,6151,6163,6173,6197,6199,6203, 6211,6217,6221,6229,6247,6257,6263,6269,6271,6277,6287,6299,6301,6311,6317, 6323,6329,6337,6343,6353,6359,6361,6367,6373,6379,6389,6397,6421,6427,6449, 6451,6469,6473,6481,6491,6521,6529,6547,6551,6553,6563,6569,6571,6577,6581, 6599,6607,6619,6637,6653,6659,6661,6673,6679,6689,6691,6701,6703,6709,6719, 6733,6737,6761,6763,6779,6781,6791,6793,6803,6823,6827,6829,6833,6841,6857, 6863,6869,6871,6883,6899,6907,6911,6917,6947,6949,6959,6961,6967,6971,6977, 6983,6991,6997,7001,7013,7019,7027,7039,7043,7057,7069,7079,7103,7109,7121, 7127,7129,7151,7159,7177,7187,7193,7207,7211,7213,7219,7229,7237,7243,7247, 7253,7283,7297,7307,7309,7321,7331,7333,7349,7351,7369,7393,7411,7417,7433, 7451,7457,7459,7477,7481,7487,7489,7499,7507,7517,7523,7529,7537,7541,7547, 7549,7559,7561,7573,7577,7583,7589,7591,7603,7607,7621,7639,7643,7649,7669, 7673,7681,7687,7691,7699,7703,7717,7723,7727,7741,7753,7757,7759,7789,7793, 7817,7823,7829,7841,7853,7867,7873,7877,7879,7883,7901,7907,7919,7927,7933, 7937,7949,7951,7963,7993,8009,8011,8017,8039,8053,8059,8069,8081,8087,8089, 8093,8101,8111,8117,8123,8147,8161,8167,8171,8179,8191,8209,8219,8221,8231, 8233,8237,8243,8263,8269,8273,8287,8291,8293,8297,8311,8317,8329,8353,8363, 8369,8377,8387,8389,8419,8423,8429,8431,8443,8447,8461,8467,8501,8513,8521, 8527,8537,8539,8543,8563,8573,8581,8597,8599,8609,8623,8627,8629,8641,8647, 8663,8669,8677,8681,8689,8693,8699,8707,8713,8719,8731,8737,8741,8747,8753, 8761,8779,8783,8803,8807,8819,8821,8831,8837,8839,8849,8861,8863,8867,8887, 8893,8923,8929,8933,8941,8951,8963,8969,8971,8999,9001,9007,9011,9013,9029, 9041,9043,9049,9059,9067,9091,9103,9109,9127,9133,9137,9151,9157,9161,9173, 9181,9187,9199,9203,9209,9221,9227,9239,9241,9257,9277,9281,9283,9293,9311, 9319,9323,9337,9341,9343,9349,9371,9377,9391,9397,9403,9413,9419,9421,9431, 9433,9437,9439,9461,9463,9467,9473,9479,9491,9497,9511,9521,9533,9539,9547, 9551,9587,9601,9613,9619,9623,9629,9631,9643,9649,9661,9677,9679,9689,9697, 9719,9721,9733,9739,9743,9749,9767,9769,9781,9787,9791,9803,9811,9817,9829, 9833,9839,9851,9857,9859,9871,9883,9887,9901,9907,9923,9929,9931,9941,9949, 9967,9973,10007,10009,10037,10039,10061,10067,10069,10079,10091,10093,10099, 10103,10111,10133,10139,10141,10151,10159,10163,10169,10177,10181,10193,10211, 10223,10243,10247,10253,10259,10267,10271,10273,10289,10301,10303,10313,10321, 10331,10333,10337,10343,10357,10369,10391,10399,10427,10429,10433,10453,10457, 10459,10463,10477,10487,10499,10501,10513,10529,10531,10559,10567,10589,10597, 10601,10607,10613,10627,10631,10639,10651,10657,10663,10667,10687,10691,10709, 10711,10723,10729,10733,10739,10753,10771,10781,10789,10799,10831,10837,10847, 10853,10859,10861,10867,10883,10889,10891,10903,10909,10937,10939,10949,10957, 10973,10979,10987,10993,11003,11027,11047,11057,11059,11069,11071,11083,11087, 11093,11113,11117,11119,11131,11149,11159,11161,11171,11173,11177,11197,11213, 11239,11243,11251,11257,11261,11273,11279,11287,11299,11311,11317,11321,11329, 11351,11353,11369,11383,11393,11399,11411,11423,11437,11443,11447,11467,11471, 11483,11489,11491,11497,11503,11519,11527,11549,11551,11579,11587,11593,11597, 11617,11621,11633,11657,11677,11681,11689,11699,11701,11717,11719,11731,11743, 11777,11779,11783,11789,11801,11807,11813,11821,11827,11831,11833,11839,11863, 11867,11887,11897,11903,11909,11923,11927,11933,11939,11941,11953,11959,11969, 11971,11981,11987,12007,12011,12037,12041,12043,12049,12071,12073,12097,12101, 12107,12109,12113,12119,12143,12149,12157,12161,12163,12197,12203,12211,12227, 12239,12241,12251,12253,12263,12269,12277,12281,12289,12301,12323,12329,12343, 12347,12373,12377,12379,12391,12401,12409,12413,12421,12433,12437,12451,12457, 12473,12479,12487,12491,12497,12503,12511,12517,12527,12539,12541,12547,12553, 12569,12577,12583,12589,12601,12611,12613,12619,12637,12641,12647,12653,12659, 12671,12689,12697,12703,12713,12721,12739,12743,12757,12763,12781,12791,12799, 12809,12821,12823,12829,12841,12853,12889,12893,12899,12907,12911,12917,12919, 12923,12941,12953,12959,12967,12973,12979,12983,13001,13003,13007,13009,13033, 13037,13043,13049,13063,13093,13099,13103,13109,13121,13127,13147,13151,13159, 13163,13171,13177,13183,13187,13217,13219,13229,13241,13249,13259,13267,13291, 13297,13309,13313,13327,13331,13337,13339,13367,13381,13397,13399,13411,13417, 13421,13441,13451,13457,13463,13469,13477,13487,13499,13513,13523,13537,13553, 13567,13577,13591,13597,13613,13619,13627,13633,13649,13669,13679,13681,13687, 13691,13693,13697,13709,13711,13721,13723,13729,13751,13757,13759,13763,13781, 13789,13799,13807,13829,13831,13841,13859,13873,13877,13879,13883,13901,13903, 13907,13913,13921,13931,13933,13963,13967,13997,13999,14009,14011,14029,14033, 14051,14057,14071,14081,14083,14087,14107,14143,14149,14153,14159,14173,14177, 14197,14207,14221,14243,14249,14251,14281,14293,14303,14321,14323,14327,14341, 14347,14369,14387,14389,14401,14407,14411,14419,14423,14431,14437,14447,14449, 14461,14479,14489,14503,14519,14533,14537,14543,14549,14551,14557,14561,14563, 14591,14593,14621,14627,14629,14633,14639,14653,14657,14669,14683,14699,14713, 14717,14723,14731,14737,14741,14747,14753,14759,14767,14771,14779,14783,14797, 14813,14821,14827,14831,14843,14851,14867,14869,14879,14887,14891,14897,14923, 14929,14939,14947,14951,14957,14969,14983,15013,15017,15031,15053,15061,15073, 15077,15083,15091,15101,15107,15121,15131,15137,15139,15149,15161,15173,15187, 15193,15199,15217,15227,15233,15241,15259,15263,15269,15271,15277,15287,15289, 15299,15307,15313,15319,15329,15331,15349,15359,15361,15373,15377,15383,15391, 15401,15413,15427,15439,15443,15451,15461,15467,15473,15493,15497,15511,15527, 15541,15551,15559,15569,15581,15583,15601,15607,15619,15629,15641,15643,15647, 15649,15661,15667,15671,15679,15683,15727,15731,15733,15737,15739,15749,15761, 15767,15773,15787,15791,15797,15803,15809,15817,15823,15859,15877,15881,15887, 15889,15901,15907,15913,15919,15923,15937,15959,15971,15973,15991,16001,16007, 16033,16057,16061,16063,16067,16069,16073,16087,16091,16097,16103,16111,16127, 16139,16141,16183,16187,16189,16193,16217,16223,16229,16231,16249,16253,16267, 16273,16301,16319,16333,16339,16349,16361,16363,16369,16381,16411,16417,16421, 16427,16433,16447,16451,16453,16477,16481,16487,16493,16519,16529,16547,16553, 16561,16567,16573,16603,16607,16619,16631,16633,16649,16651,16657,16661,16673, 16691,16693,16699,16703,16729,16741,16747,16759,16763,16787,16811,16823,16829, 16831,16843,16871,16879,16883,16889,16901,16903,16921,16927,16931,16937,16943, 16963,16979,16981,16987,16993,17011,17021,17027,17029,17033,17041,17047,17053, 17077,17093,17099,17107,17117,17123,17137,17159,17167,17183,17189,17191,17203, 17207,17209,17231,17239,17257,17291,17293,17299,17317,17321,17327,17333,17341, 17351,17359,17377,17383,17387,17389,17393,17401,17417,17419,17431,17443,17449, 17467,17471,17477,17483,17489,17491,17497,17509,17519,17539,17551,17569,17573, 17579,17581,17597,17599,17609,17623,17627,17657,17659,17669,17681,17683,17707, 17713,17729,17737,17747,17749,17761,17783,17789,17791,17807,17827,17837,17839, 17851,17863,17881,17891,17903,17909,17911,17921,17923,17929,17939,17957,17959, 17971,17977,17981,17987,17989,18013,18041,18043,18047,18049,18059,18061,18077, 18089,18097,18119,18121,18127,18131,18133,18143,18149,18169,18181,18191,18199, 18211,18217,18223,18229,18233,18251,18253,18257,18269,18287,18289,18301,18307, 18311,18313,18329,18341,18353,18367,18371,18379,18397,18401,18413,18427,18433, 18439,18443,18451,18457,18461,18481,18493,18503,18517,18521,18523,18539,18541, 18553,18583,18587,18593,18617,18637,18661,18671,18679,18691,18701,18713,18719, 18731,18743,18749,18757,18773,18787,18793,18797,18803,18839,18859,18869,18899, 18911,18913,18917,18919,18947,18959,18973,18979,19001,19009,19013,19031,19037, 19051,19069,19073,19079,19081,19087,19121,19139,19141,19157,19163,19181,19183, 19207,19211,19213,19219,19231,19237,19249,19259,19267,19273,19289,19301,19309, 19319,19333,19373,19379,19381,19387,19391,19403,19417,19421,19423,19427,19429, 19433,19441,19447,19457,19463,19469,19471,19477,19483,19489,19501,19507,19531, 19541,19543,19553,19559,19571,19577,19583,19597,19603,19609,19661,19681,19687, 19697,19699,19709,19717,19727,19739,19751,19753,19759,19763,19777,19793,19801, 19813,19819,19841,19843,19853,19861,19867,19889,19891,19913,19919,19927,19937, 19949,19961,19963,19973,19979,19991,19993,19997,20011,20021,20023,20029,20047, 20051,20063,20071,20089,20101,20107,20113,20117,20123,20129,20143,20147,20149, 20161,20173,20177,20183,20201,20219,20231,20233,20249,20261,20269,20287,20297, 20323,20327,20333,20341,20347,20353,20357,20359,20369,20389,20393,20399,20407, 20411,20431,20441,20443,20477,20479,20483,20507,20509,20521,20533,20543,20549, 20551,20563,20593,20599,20611,20627,20639,20641,20663,20681,20693,20707,20717, 20719,20731,20743,20747,20749,20753,20759,20771,20773,20789,20807,20809,20849, 20857,20873,20879,20887,20897,20899,20903,20921,20929,20939,20947,20959,20963, 20981,20983,21001,21011,21013,21017,21019,21023,21031,21059,21061,21067,21089, 21101,21107,21121,21139,21143,21149,21157,21163,21169,21179,21187,21191,21193, 21211,21221,21227,21247,21269,21277,21283,21313,21317,21319,21323,21341,21347, 21377,21379,21383,21391,21397,21401,21407,21419,21433,21467,21481,21487,21491, 21493,21499,21503,21517,21521,21523,21529,21557,21559,21563,21569,21577,21587, 21589,21599,21601,21611,21613,21617,21647,21649,21661,21673,21683,21701,21713, 21727,21737,21739,21751,21757,21767,21773,21787,21799,21803,21817,21821,21839, 21841,21851,21859,21863,21871,21881,21893,21911,21929,21937,21943,21961,21977, 21991,21997,22003,22013,22027,22031,22037,22039,22051,22063,22067,22073,22079, 22091,22093,22109,22111,22123,22129,22133,22147,22153,22157,22159,22171,22189, 22193,22229,22247,22259,22271,22273,22277,22279,22283,22291,22303,22307,22343, 22349,22367,22369,22381,22391,22397,22409,22433,22441,22447,22453,22469,22481, 22483,22501,22511,22531,22541,22543,22549,22567,22571,22573,22613,22619,22621, 22637,22639,22643,22651,22669,22679,22691,22697,22699,22709,22717,22721,22727, 22739,22741,22751,22769,22777,22783,22787,22807,22811,22817,22853,22859,22861, 22871,22877,22901,22907,22921,22937,22943,22961,22963,22973,22993,23003,23011, 23017,23021,23027,23029,23039,23041,23053,23057,23059,23063,23071,23081,23087, 23099,23117,23131,23143,23159,23167,23173,23189,23197,23201,23203,23209,23227, 23251,23269,23279,23291,23293,23297,23311,23321,23327,23333,23339,23357,23369, 23371,23399,23417,23431,23447,23459,23473,23497,23509,23531,23537,23539,23549, 23557,23561,23563,23567,23581,23593,23599,23603,23609,23623,23627,23629,23633, 23663,23669,23671,23677,23687,23689,23719,23741,23743,23747,23753,23761,23767, 23773,23789,23801,23813,23819,23827,23831,23833,23857,23869,23873,23879,23887, 23893,23899,23909,23911,23917,23929,23957,23971,23977,23981,23993,24001,24007, 24019,24023,24029,24043,24049,24061,24071,24077,24083,24091,24097,24103,24107, 24109,24113,24121,24133,24137,24151,24169,24179,24181,24197,24203,24223,24229, 24239,24247,24251,24281,24317,24329,24337,24359,24371,24373,24379,24391,24407, 24413,24419,24421,24439,24443,24469,24473,24481,24499,24509,24517,24527,24533, 24547,24551,24571,24593,24611,24623,24631,24659,24671,24677,24683,24691,24697, 24709,24733,24749,24763,24767,24781,24793,24799,24809,24821,24841,24847,24851, 24859,24877,24889,24907,24917,24919,24923,24943,24953,24967,24971,24977,24979, 24989,25013,25031,25033,25037,25057,25073,25087,25097,25111,25117,25121,25127, 25147,25153,25163,25169,25171,25183,25189,25219,25229,25237,25243,25247,25253, 25261,25301,25303,25307,25309,25321,25339,25343,25349,25357,25367,25373,25391, 25409,25411,25423,25439,25447,25453,25457,25463,25469,25471,25523,25537,25541, 25561,25577,25579,25583,25589,25601,25603,25609,25621,25633,25639,25643,25657, 25667,25673,25679,25693,25703,25717,25733,25741,25747,25759,25763,25771,25793, 25799,25801,25819,25841,25847,25849,25867,25873,25889,25903,25913,25919,25931, 25933,25939,25943,25951,25969,25981,25997,25999,26003,26017,26021,26029,26041, 26053,26083,26099,26107,26111,26113,26119,26141,26153,26161,26171,26177,26183, 26189,26203,26209,26227,26237,26249,26251,26261,26263,26267,26293,26297,26309, 26317,26321,26339,26347,26357,26371,26387,26393,26399,26407,26417,26423,26431, 26437,26449,26459,26479,26489,26497,26501,26513,26539,26557,26561,26573,26591, 26597,26627,26633,26641,26647,26669,26681,26683,26687,26693,26699,26701,26711, 26713,26717,26723,26729,26731,26737,26759,26777,26783,26801,26813,26821,26833, 26839,26849,26861,26863,26879,26881,26891,26893,26903,26921,26927,26947,26951, 26953,26959,26981,26987,26993,27011,27017,27031,27043,27059,27061,27067,27073, 27077,27091,27103,27107,27109,27127,27143,27179,27191,27197,27211,27239,27241, 27253,27259,27271,27277,27281,27283,27299,27329,27337,27361,27367,27397,27407, 27409,27427,27431,27437,27449,27457,27479,27481,27487,27509,27527,27529,27539, 27541,27551,27581,27583,27611,27617,27631,27647,27653,27673,27689,27691,27697, 27701,27733,27737,27739,27743,27749,27751,27763,27767,27773,27779,27791,27793, 27799,27803,27809,27817,27823,27827,27847,27851,27883,27893,27901,27917,27919, 27941,27943,27947,27953,27961,27967,27983,27997,28001,28019,28027,28031,28051, 28057,28069,28081,28087,28097,28099,28109,28111,28123,28151,28163,28181,28183, 28201,28211,28219,28229,28277,28279,28283,28289,28297,28307,28309,28319,28349, 28351,28387,28393,28403,28409,28411,28429,28433,28439,28447,28463,28477,28493, 28499,28513,28517,28537,28541,28547,28549,28559,28571,28573,28579,28591,28597, 28603,28607,28619,28621,28627,28631,28643,28649,28657,28661,28663,28669,28687, 28697,28703,28711,28723,28729,28751,28753,28759,28771,28789,28793,28807,28813, 28817,28837,28843,28859,28867,28871,28879,28901,28909,28921,28927,28933,28949, 28961,28979,29009,29017,29021,29023,29027,29033,29059,29063,29077,29101,29123, 29129,29131,29137,29147,29153,29167,29173,29179,29191,29201,29207,29209,29221, 29231,29243,29251,29269,29287,29297,29303,29311,29327,29333,29339,29347,29363, 29383,29387,29389,29399,29401,29411,29423,29429,29437,29443,29453,29473,29483, 29501,29527,29531,29537,29567,29569,29573,29581,29587,29599,29611,29629,29633, 29641,29663,29669,29671,29683,29717,29723,29741,29753,29759,29761,29789,29803, 29819,29833,29837,29851,29863,29867,29873,29879,29881,29917,29921,29927,29947, 29959,29983,29989,30011,30013,30029,30047,30059,30071,30089,30091,30097,30103, 30109,30113,30119,30133,30137,30139,30161,30169,30181,30187,30197,30203,30211, 30223,30241,30253,30259,30269,30271,30293,30307,30313,30319,30323,30341,30347, 30367,30389,30391,30403,30427,30431,30449,30467,30469,30491,30493,30497,30509, 30517,30529,30539,30553,30557,30559,30577,30593,30631,30637,30643,30649,30661, 30671,30677,30689,30697,30703,30707,30713,30727,30757,30763,30773,30781,30803, 30809,30817,30829,30839,30841,30851,30853,30859,30869,30871,30881,30893,30911, 30931,30937,30941,30949,30971,30977,30983,31013,31019,31033,31039,31051,31063, 31069,31079,31081,31091,31121,31123,31139,31147,31151,31153,31159,31177,31181, 31183,31189,31193,31219,31223,31231,31237,31247,31249,31253,31259,31267,31271, 31277,31307,31319,31321,31327,31333,31337,31357,31379,31387,31391,31393,31397, 31469,31477,31481,31489,31511,31513,31517,31531,31541,31543,31547,31567,31573, 31583,31601,31607,31627,31643,31649,31657,31663,31667,31687,31699,31721,31723, 31727,31729,31741,31751,31769,31771,31793,31799,31817,31847,31849,31859,31873, 31883,31891,31907,31957,31963,31973,31981,31991,32003,32009,32027,32029,32051, 32057,32059,32063,32069,32077,32083,32089,32099,32117,32119,32141,32143,32159, 32173,32183,32189,32191,32203,32213,32233,32237,32251,32257,32261,32297,32299, 32303,32309,32321,32323,32327,32341,32353,32359,32363,32369,32371,32377,32381, 32401,32411,32413,32423,32429,32441,32443,32467,32479,32491,32497,32503,32507, 32531,32533,32537,32561,32563,32569,32573,32579,32587,32603,32609,32611,32621, 32633,32647,32653,32687,32693,32707,32713,32717,32719,32749,32771,32779,32783, 32789,32797,32801,32803,32831,32833,32839,32843,32869,32887,32909,32911,32917, 32933,32939,32941,32957,32969,32971,32983,32987,32993,32999,33013,33023,33029, 33037,33049,33053,33071,33073,33083,33091,33107,33113,33119,33149,33151,33161, 33179,33181,33191,33199,33203,33211,33223,33247,33287,33289,33301,33311,33317, 33329,33331,33343,33347,33349,33353,33359,33377,33391,33403,33409,33413,33427, 33457,33461,33469,33479,33487,33493,33503,33521,33529,33533,33547,33563,33569, 33577,33581,33587,33589,33599,33601,33613,33617,33619,33623,33629,33637,33641, 33647,33679,33703,33713,33721,33739,33749,33751,33757,33767,33769,33773,33791, 33797,33809,33811,33827,33829,33851,33857,33863,33871,33889,33893,33911,33923, 33931,33937,33941,33961,33967,33997,34019,34031,34033,34039,34057,34061,34123, 34127,34129,34141,34147,34157,34159,34171,34183,34211,34213,34217,34231,34253, 34259,34261,34267,34273,34283,34297,34301,34303,34313,34319,34327,34337,34351, 34361,34367,34369,34381,34403,34421,34429,34439,34457,34469,34471,34483,34487, 34499,34501,34511,34513,34519,34537,34543,34549,34583,34589,34591,34603,34607, 34613,34631,34649,34651,34667,34673,34679,34687,34693,34703,34721,34729,34739, 34747,34757,34759,34763,34781,34807,34819,34841,34843,34847,34849,34871,34877, 34883,34897,34913,34919,34939,34949,34961,34963,34981,35023,35027,35051,35053, 35059,35069,35081,35083,35089,35099,35107,35111,35117,35129,35141,35149,35153, 35159,35171,35201,35221,35227,35251,35257,35267,35279,35281,35291,35311,35317, 35323,35327,35339,35353,35363,35381,35393,35401,35407,35419,35423,35437,35447, 35449,35461,35491,35507,35509,35521,35527,35531,35533,35537,35543,35569,35573, 35591,35593,35597,35603,35617,35671,35677,35729,35731,35747,35753,35759,35771, 35797,35801,35803,35809,35831,35837,35839,35851,35863,35869,35879,35897,35899, 35911,35923,35933,35951,35963,35969,35977,35983,35993,35999,36007,36011,36013, 36017,36037,36061,36067,36073,36083,36097,36107,36109,36131,36137,36151,36161, 36187,36191,36209,36217,36229,36241,36251,36263,36269,36277,36293,36299,36307, 36313,36319,36341,36343,36353,36373,36383,36389,36433,36451,36457,36467,36469, 36473,36479,36493,36497,36523,36527,36529,36541,36551,36559,36563,36571,36583, 36587,36599,36607,36629,36637,36643,36653,36671,36677,36683,36691,36697,36709, 36713,36721,36739,36749,36761,36767,36779,36781,36787,36791,36793,36809,36821, 36833,36847,36857,36871,36877,36887,36899,36901,36913,36919,36923,36929,36931, 36943,36947,36973,36979,36997,37003,37013,37019,37021,37039,37049,37057,37061, 37087,37097,37117,37123,37139,37159,37171,37181,37189,37199,37201,37217,37223, 37243,37253,37273,37277,37307,37309,37313,37321,37337,37339,37357,37361,37363, 37369,37379,37397,37409,37423,37441,37447,37463,37483,37489,37493,37501,37507, 37511,37517,37529,37537,37547,37549,37561,37567,37571,37573,37579,37589,37591, 37607,37619,37633,37643,37649,37657,37663,37691,37693,37699,37717,37747,37781, 37783,37799,37811,37813,37831,37847,37853,37861,37871,37879,37889,37897,37907, 37951,37957,37963,37967,37987,37991,37993,37997,38011,38039,38047,38053,38069, 38083,38113,38119,38149,38153,38167,38177,38183,38189,38197,38201,38219,38231, 38237,38239,38261,38273,38281,38287,38299,38303,38317,38321,38327,38329,38333, 38351,38371,38377,38393,38431,38447,38449,38453,38459,38461,38501,38543,38557, 38561,38567,38569,38593,38603,38609,38611,38629,38639,38651,38653,38669,38671, 38677,38693,38699,38707,38711,38713,38723,38729,38737,38747,38749,38767,38783, 38791,38803,38821,38833,38839,38851,38861,38867,38873,38891,38903,38917,38921, 38923,38933,38953,38959,38971,38977,38993,39019,39023,39041,39043,39047,39079, 39089,39097,39103,39107,39113,39119,39133,39139,39157,39161,39163,39181,39191, 39199,39209,39217,39227,39229,39233,39239,39241,39251,39293,39301,39313,39317, 39323,39341,39343,39359,39367,39371,39373,39383,39397,39409,39419,39439,39443, 39451,39461,39499,39503,39509,39511,39521,39541,39551,39563,39569,39581,39607, 39619,39623,39631,39659,39667,39671,39679,39703,39709,39719,39727,39733,39749, 39761,39769,39779,39791,39799,39821,39827,39829,39839,39841,39847,39857,39863, 39869,39877,39883,39887,39901,39929,39937,39953,39971,39979,39983,39989,40009, 40013,40031,40037,40039,40063,40087,40093,40099,40111,40123,40127,40129,40151, 40153,40163,40169,40177,40189,40193,40213,40231,40237,40241,40253,40277,40283, 40289,40343,40351,40357,40361,40387,40423,40427,40429,40433,40459,40471,40483, 40487,40493,40499,40507,40519,40529,40531,40543,40559,40577,40583,40591,40597, 40609,40627,40637,40639,40693,40697,40699,40709,40739,40751,40759,40763,40771, 40787,40801,40813,40819,40823,40829,40841,40847,40849,40853,40867,40879,40883, 40897,40903,40927,40933,40939,40949,40961,40973,40993,41011,41017,41023,41039, 41047,41051,41057,41077,41081,41113,41117,41131,41141,41143,41149,41161,41177, 41179,41183,41189,41201,41203,41213,41221,41227,41231,41233,41243,41257,41263, 41269,41281,41299,41333,41341,41351,41357,41381,41387,41389,41399,41411,41413, 41443,41453,41467,41479,41491,41507,41513,41519,41521,41539,41543,41549,41579, 41593,41597,41603,41609,41611,41617,41621,41627,41641,41647,41651,41659,41669, 41681,41687,41719,41729,41737,41759,41761,41771,41777,41801,41809,41813,41843, 41849,41851,41863,41879,41887,41893,41897,41903,41911,41927,41941,41947,41953, 41957,41959,41969,41981,41983,41999,42013,42017,42019,42023,42043,42061,42071, 42073,42083,42089,42101,42131,42139,42157,42169,42179,42181,42187,42193,42197, 42209,42221,42223,42227,42239,42257,42281,42283,42293,42299,42307,42323,42331, 42337,42349,42359,42373,42379,42391,42397,42403,42407,42409,42433,42437,42443, 42451,42457,42461,42463,42467,42473,42487,42491,42499,42509,42533,42557,42569, 42571,42577,42589,42611,42641,42643,42649,42667,42677,42683,42689,42697,42701, 42703,42709,42719,42727,42737,42743,42751,42767,42773,42787,42793,42797,42821, 42829,42839,42841,42853,42859,42863,42899,42901,42923,42929,42937,42943,42953, 42961,42967,42979,42989,43003,43013,43019,43037,43049,43051,43063,43067,43093, 43103,43117,43133,43151,43159,43177,43189,43201,43207,43223,43237,43261,43271, 43283,43291,43313,43319,43321,43331,43391,43397,43399,43403,43411,43427,43441, 43451,43457,43481,43487,43499,43517,43541,43543,43573,43577,43579,43591,43597, 43607,43609,43613,43627,43633,43649,43651,43661,43669,43691,43711,43717,43721, 43753,43759,43777,43781,43783,43787,43789,43793,43801,43853,43867,43889,43891, 43913,43933,43943,43951,43961,43963,43969,43973,43987,43991,43997,44017,44021, 44027,44029,44041,44053,44059,44071,44087,44089,44101,44111,44119,44123,44129, 44131,44159,44171,44179,44189,44201,44203,44207,44221,44249,44257,44263,44267, 44269,44273,44279,44281,44293,44351,44357,44371,44381,44383,44389,44417,44449, 44453,44483,44491,44497,44501,44507,44519,44531,44533,44537,44543,44549,44563, 44579,44587,44617,44621,44623,44633,44641,44647,44651,44657,44683,44687,44699, 44701,44711,44729,44741,44753,44771,44773,44777,44789,44797,44809,44819,44839, 44843,44851,44867,44879,44887,44893,44909,44917,44927,44939,44953,44959,44963, 44971,44983,44987,45007,45013,45053,45061,45077,45083,45119,45121,45127,45131, 45137,45139,45161,45179,45181,45191,45197,45233,45247,45259,45263,45281,45289, 45293,45307,45317,45319,45329,45337,45341,45343,45361,45377,45389,45403,45413, 45427,45433,45439,45481,45491,45497,45503,45523,45533,45541,45553,45557,45569, 45587,45589,45599,45613,45631,45641,45659,45667,45673,45677,45691,45697,45707, 45737,45751,45757,45763,45767,45779,45817,45821,45823,45827,45833,45841,45853, 45863,45869,45887,45893,45943,45949,45953,45959,45971,45979,45989,46021,46027, 46049,46051,46061,46073,46091,46093,46099,46103,46133,46141,46147,46153,46171, 46181,46183,46187,46199,46219,46229,46237,46261,46271,46273,46279,46301,46307, 46309,46327,46337,46349,46351,46381,46399,46411,46439,46441,46447,46451,46457, 46471,46477,46489,46499,46507,46511,46523,46549,46559,46567,46573,46589,46591, 46601,46619,46633,46639,46643,46649,46663,46679,46681,46687,46691,46703,46723, 46727,46747,46751,46757,46769,46771,46807,46811,46817,46819,46829,46831,46853, 46861,46867,46877,46889,46901,46919,46933,46957,46993,46997,47017,47041,47051, 47057,47059,47087,47093,47111,47119,47123,47129,47137,47143,47147,47149,47161, 47189,47207,47221,47237,47251,47269,47279,47287,47293,47297,47303,47309,47317, 47339,47351,47353,47363,47381,47387,47389,47407,47417,47419,47431,47441,47459, 47491,47497,47501,47507,47513,47521,47527,47533,47543,47563,47569,47581,47591, 47599,47609,47623,47629,47639,47653,47657,47659,47681,47699,47701,47711,47713, 47717,47737,47741,47743,47777,47779,47791,47797,47807,47809,47819,47837,47843, 47857,47869,47881,47903,47911,47917,47933,47939,47947,47951,47963,47969,47977, 47981,48017,48023,48029,48049,48073,48079,48091,48109,48119,48121,48131,48157, 48163,48179,48187,48193,48197,48221,48239,48247,48259,48271,48281,48299,48311, 48313,48337,48341,48353,48371,48383,48397,48407,48409,48413,48437,48449,48463, 48473,48479,48481,48487,48491,48497,48523,48527,48533,48539,48541,48563,48571, 48589,48593,48611,48619,48623,48647,48649,48661,48673,48677,48679,48731,48733, 48751,48757,48761,48767,48779,48781,48787,48799,48809,48817,48821,48823,48847, 48857,48859,48869,48871,48883,48889,48907,48947,48953,48973,48989,48991,49003, 49009,49019,49031,49033,49037,49043,49057,49069,49081,49103,49109,49117,49121, 49123,49139,49157,49169,49171,49177,49193,49199,49201,49207,49211,49223,49253, 49261,49277,49279,49297,49307,49331,49333,49339,49363,49367,49369,49391,49393, 49409,49411,49417,49429,49433,49451,49459,49463,49477,49481,49499,49523,49529, 49531,49537,49547,49549,49559,49597,49603,49613,49627,49633,49639,49663,49667, 49669,49681,49697,49711,49727,49739,49741,49747,49757,49783,49787,49789,49801, 49807,49811,49823,49831,49843,49853,49871,49877,49891,49919,49921,49927,49937, 49939,49943,49957,49991,49993,49999,50021,50023,50033,50047,50051,50053,50069, 50077,50087,50093,50101,50111,50119,50123,50129,50131,50147,50153,50159,50177, 50207,50221,50227,50231,50261,50263,50273,50287,50291,50311,50321,50329,50333, 50341,50359,50363,50377,50383,50387,50411,50417,50423,50441,50459,50461,50497, 50503,50513,50527,50539,50543,50549,50551,50581,50587,50591,50593,50599,50627, 50647,50651,50671,50683,50707,50723,50741,50753,50767,50773,50777,50789,50821, 50833,50839,50849,50857,50867,50873,50891,50893,50909,50923,50929,50951,50957, 50969,50971,50989,50993,51001,51031,51043,51047,51059,51061,51071,51109,51131, 51133,51137,51151,51157,51169,51193,51197,51199,51203,51217,51229,51239,51241, 51257,51263,51283,51287,51307,51329,51341,51343,51347,51349,51361,51383,51407, 51413,51419,51421,51427,51431,51437,51439,51449,51461,51473,51479,51481,51487, 51503,51511,51517,51521,51539,51551,51563,51577,51581,51593,51599,51607,51613, 51631,51637,51647,51659,51673,51679,51683,51691,51713,51719,51721,51749,51767, 51769,51787,51797,51803,51817,51827,51829,51839,51853,51859,51869,51871,51893, 51899,51907,51913,51929,51941,51949,51971,51973,51977,51991,52009,52021,52027, 52051,52057,52067,52069,52081,52103,52121,52127,52147,52153,52163,52177,52181, 52183,52189,52201,52223,52237,52249,52253,52259,52267,52289,52291,52301,52313, 52321,52361,52363,52369,52379,52387,52391,52433,52453,52457,52489,52501,52511, 52517,52529,52541,52543,52553,52561,52567,52571,52579,52583,52609,52627,52631, 52639,52667,52673,52691,52697,52709,52711,52721,52727,52733,52747,52757,52769, 52783,52807,52813,52817,52837,52859,52861,52879,52883,52889,52901,52903,52919, 52937,52951,52957,52963,52967,52973,52981,52999,53003,53017,53047,53051,53069, 53077,53087,53089,53093,53101,53113,53117,53129,53147,53149,53161,53171,53173, 53189,53197,53201,53231,53233,53239,53267,53269,53279,53281,53299,53309,53323, 53327,53353,53359,53377,53381,53401,53407,53411,53419,53437,53441,53453,53479, 53503,53507,53527,53549,53551,53569,53591,53593,53597,53609,53611,53617,53623, 53629,53633,53639,53653,53657,53681,53693,53699,53717,53719,53731,53759,53773, 53777,53783,53791,53813,53819,53831,53849,53857,53861,53881,53887,53891,53897, 53899,53917,53923,53927,53939,53951,53959,53987,53993,54001,54011,54013,54037, 54049,54059,54083,54091,54101,54121,54133,54139,54151,54163,54167,54181,54193, 54217,54251,54269,54277,54287,54293,54311,54319,54323,54331,54347,54361,54367, 54371,54377,54401,54403,54409,54413,54419,54421,54437,54443,54449,54469,54493, 54497,54499,54503,54517,54521,54539,54541,54547,54559,54563,54577,54581,54583, 54601,54617,54623,54629,54631,54647,54667,54673,54679,54709,54713,54721,54727, 54751,54767,54773,54779,54787,54799,54829,54833,54851,54869,54877,54881,54907, 54917,54919,54941,54949,54959,54973,54979,54983,55001,55009,55021,55049,55051, 55057,55061,55073,55079,55103,55109,55117,55127,55147,55163,55171,55201,55207, 55213,55217,55219,55229,55243,55249,55259,55291,55313,55331,55333,55337,55339, 55343,55351,55373,55381,55399,55411,55439,55441,55457,55469,55487,55501,55511, 55529,55541,55547,55579,55589,55603,55609,55619,55621,55631,55633,55639,55661, 55663,55667,55673,55681,55691,55697,55711,55717,55721,55733,55763,55787,55793, 55799,55807,55813,55817,55819,55823,55829,55837,55843,55849,55871,55889,55897, 55901,55903,55921,55927,55931,55933,55949,55967,55987,55997,56003,56009,56039, 56041,56053,56081,56087,56093,56099,56101,56113,56123,56131,56149,56167,56171, 56179,56197,56207,56209,56237,56239,56249,56263,56267,56269,56299,56311,56333, 56359,56369,56377,56383,56393,56401,56417,56431,56437,56443,56453,56467,56473, 56477,56479,56489,56501,56503,56509,56519,56527,56531,56533,56543,56569,56591, 56597,56599,56611,56629,56633,56659,56663,56671,56681,56687,56701,56711,56713, 56731,56737,56747,56767,56773,56779,56783,56807,56809,56813,56821,56827,56843, 56857,56873,56891,56893,56897,56909,56911,56921,56923,56929,56941,56951,56957, 56963,56983,56989,56993,56999,57037,57041,57047,57059,57073,57077,57089,57097, 57107,57119,57131,57139,57143,57149,57163,57173,57179,57191,57193,57203,57221, 57223,57241,57251,57259,57269,57271,57283,57287,57301,57329,57331,57347,57349, 57367,57373,57383,57389,57397,57413,57427,57457,57467,57487,57493,57503,57527, 57529,57557,57559,57571,57587,57593,57601,57637,57641,57649,57653,57667,57679, 57689,57697,57709,57713,57719,57727,57731,57737,57751,57773,57781,57787,57791, 57793,57803,57809,57829,57839,57847,57853,57859,57881,57899,57901,57917,57923, 57943,57947,57973,57977,57991,58013,58027,58031,58043,58049,58057,58061,58067, 58073,58099,58109,58111,58129,58147,58151,58153,58169,58171,58189,58193,58199, 58207,58211,58217,58229,58231,58237,58243,58271,58309,58313,58321,58337,58363, 58367,58369,58379,58391,58393,58403,58411,58417,58427,58439,58441,58451,58453, 58477,58481,58511,58537,58543,58549,58567,58573,58579,58601,58603,58613,58631, 58657,58661,58679,58687,58693,58699,58711,58727,58733,58741,58757,58763,58771, 58787,58789,58831,58889,58897,58901,58907,58909,58913,58921,58937,58943,58963, 58967,58979,58991,58997,59009,59011,59021,59023,59029,59051,59053,59063,59069, 59077,59083,59093,59107,59113,59119,59123,59141,59149,59159,59167,59183,59197, 59207,59209,59219,59221,59233,59239,59243,59263,59273,59281,59333,59341,59351, 59357,59359,59369,59377,59387,59393,59399,59407,59417,59419,59441,59443,59447, 59453,59467,59471,59473,59497,59509,59513,59539,59557,59561,59567,59581,59611, 59617,59621,59627,59629,59651,59659,59663,59669,59671,59693,59699,59707,59723, 59729,59743,59747,59753,59771,59779,59791,59797,59809,59833,59863,59879,59887, 59921,59929,59951,59957,59971,59981,59999,60013,60017,60029,60037,60041,60077, 60083,60089,60091,60101,60103,60107,60127,60133,60139,60149,60161,60167,60169, 60209,60217,60223,60251,60257,60259,60271,60289,60293,60317,60331,60337,60343, 60353,60373,60383,60397,60413,60427,60443,60449,60457,60493,60497,60509,60521, 60527,60539,60589,60601,60607,60611,60617,60623,60631,60637,60647,60649,60659, 60661,60679,60689,60703,60719,60727,60733,60737,60757,60761,60763,60773,60779, 60793,60811,60821,60859,60869,60887,60889,60899,60901,60913,60917,60919,60923, 60937,60943,60953,60961,61001,61007,61027,61031,61043,61051,61057,61091,61099, 61121,61129,61141,61151,61153,61169,61211,61223,61231,61253,61261,61283,61291, 61297,61331,61333,61339,61343,61357,61363,61379,61381,61403,61409,61417,61441, 61463,61469,61471,61483,61487,61493,61507,61511,61519,61543,61547,61553,61559, 61561,61583,61603,61609,61613,61627,61631,61637,61643,61651,61657,61667,61673, 61681,61687,61703,61717,61723,61729,61751,61757,61781,61813,61819,61837,61843, 61861,61871,61879,61909,61927,61933,61949,61961,61967,61979,61981,61987,61991, 62003,62011,62017,62039,62047,62053,62057,62071,62081,62099,62119,62129,62131, 62137,62141,62143,62171,62189,62191,62201,62207,62213,62219,62233,62273,62297, 62299,62303,62311,62323,62327,62347,62351,62383,62401,62417,62423,62459,62467, 62473,62477,62483,62497,62501,62507,62533,62539,62549,62563,62581,62591,62597, 62603,62617,62627,62633,62639,62653,62659,62683,62687,62701,62723,62731,62743, 62753,62761,62773,62791,62801,62819,62827,62851,62861,62869,62873,62897,62903, 62921,62927,62929,62939,62969,62971,62981,62983,62987,62989,63029,63031,63059, 63067,63073,63079,63097,63103,63113,63127,63131,63149,63179,63197,63199,63211, 63241,63247,63277,63281,63299,63311,63313,63317,63331,63337,63347,63353,63361, 63367,63377,63389,63391,63397,63409,63419,63421,63439,63443,63463,63467,63473, 63487,63493,63499,63521,63527,63533,63541,63559,63577,63587,63589,63599,63601, 63607,63611,63617,63629,63647,63649,63659,63667,63671,63689,63691,63697,63703, 63709,63719,63727,63737,63743,63761,63773,63781,63793,63799,63803,63809,63823, 63839,63841,63853,63857,63863,63901,63907,63913,63929,63949,63977,63997,64007, 64013,64019,64033,64037,64063,64067,64081,64091,64109,64123,64151,64153,64157, 64171,64187,64189,64217,64223,64231,64237,64271,64279,64283,64301,64303,64319, 64327,64333,64373,64381,64399,64403,64433,64439,64451,64453,64483,64489,64499, 64513,64553,64567,64577,64579,64591,64601,64609,64613,64621,64627,64633,64661, 64663,64667,64679,64693,64709,64717,64747,64763,64781,64783,64793,64811,64817, 64849,64853,64871,64877,64879,64891,64901,64919,64921,64927,64937,64951,64969, 64997,65003,65011,65027,65029,65033,65053,65063,65071,65089,65099,65101,65111, 65119,65123,65129,65141,65147,65167,65171,65173,65179,65183,65203,65213,65239, 65257,65267,65269,65287,65293,65309,65323,65327,65353,65357,65371,65381,65393, 65407,65413,65419,65423,65437,65447,65449,65479,65497,65519,65521,65537 }; /* pr_limit - largest prime in the prime table */ const ubig *const pr_limit = &prime[(sizeof(prime)/sizeof(prime[0]))-1]; diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c index 445ea213ba2a..f5c9bb6e1967 100644 --- a/usr.bin/procstat/procstat.c +++ b/usr.bin/procstat/procstat.c @@ -1,632 +1,631 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2007, 2011 Robert N. M. Watson * Copyright (c) 2015 Allan Jude * Copyright (c) 2017 Dell EMC * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include "procstat.h" enum { PS_CMP_NORMAL = 0x00, PS_CMP_PLURAL = 0x01, PS_CMP_SUBSTR = 0x02 }; struct procstat_cmd { const char *command; const char *xocontainer; const char *usage; void (*cmd)(struct procstat *, struct kinfo_proc *); void (*opt)(int, char * const *); int cmp; }; int procstat_opts = 0; static void cmdopt_none(int argc, char * const argv[]); static void cmdopt_verbose(int argc, char * const argv[]); static void cmdopt_signals(int argc, char * const argv[]); static void cmdopt_rusage(int argc, char * const argv[]); static void cmdopt_files(int argc, char * const argv[]); static void cmdopt_cpuset(int argc, char * const argv[]); static const char *progname; /* aliased program parameters and arguments * - usage field is abused to hold the pointer to the function * displaying program usage */ static const struct procstat_cmd pacmd_table[] = { /* arguments are the same as for pwdx: pid or core file */ { "pargs", "args", NULL, &procstat_pargs, &cmdopt_none, PS_CMP_NORMAL | PS_MODE_COMPAT }, { "penv", "env", NULL, &procstat_penv, &cmdopt_none, PS_CMP_NORMAL | PS_MODE_COMPAT }, { "pwdx", "pwd", NULL, &procstat_pwdx, &cmdopt_none, PS_CMP_NORMAL | PS_MODE_COMPAT } }; /* procstat parameters and arguments */ static const struct procstat_cmd cmd_table[] = { { "advlock", "advisory_locks", NULL, &procstat_advlocks, &cmdopt_none, PS_CMP_PLURAL | PS_CMP_SUBSTR | PS_MODE_NO_KINFO_PROC }, { "argument", "arguments", NULL, &procstat_args, &cmdopt_none, PS_CMP_PLURAL | PS_CMP_SUBSTR }, { "auxv", "auxv", NULL, &procstat_auxv, &cmdopt_none, PS_CMP_NORMAL }, { "basic", "basic", NULL, &procstat_basic, &cmdopt_none, PS_CMP_NORMAL }, { "binary", "binary", NULL, &procstat_bin, &cmdopt_none, PS_CMP_SUBSTR }, { "cpuset", "cs", NULL, &procstat_cs, &cmdopt_cpuset, PS_CMP_NORMAL }, { "cs", "cs", NULL, &procstat_cs, &cmdopt_cpuset, PS_CMP_NORMAL }, { "credential", "credentials", NULL, &procstat_cred, &cmdopt_none, PS_CMP_PLURAL | PS_CMP_SUBSTR }, { "environment", "environment", NULL, &procstat_env, &cmdopt_none, PS_CMP_SUBSTR }, { "fd", "files", "[-C]", &procstat_files, &cmdopt_files, PS_CMP_PLURAL }, { "file", "files", "[-C]", &procstat_files, &cmdopt_files, PS_CMP_PLURAL }, { "kstack", "kstack", "[-v]", &procstat_kstack, &cmdopt_verbose, PS_CMP_NORMAL }, { "pargs", "args", NULL, &procstat_pargs, &cmdopt_none, PS_CMP_NORMAL }, { "penv", "env", NULL, &procstat_penv, &cmdopt_none, PS_CMP_NORMAL }, { "ptlwpinfo", "ptlwpinfo", NULL, &procstat_ptlwpinfo, &cmdopt_none, PS_CMP_NORMAL }, { "pwdx", "pwd", NULL, &procstat_pwdx, &cmdopt_none, PS_CMP_NORMAL }, { "rlimit", "rlimit", NULL, &procstat_rlimit, &cmdopt_none, PS_CMP_NORMAL }, { "rusage", "rusage", "[-Ht]", &procstat_rusage, &cmdopt_rusage, PS_CMP_NORMAL }, { "sigfastblock", "sigfastblock", NULL, &procstat_sigfastblock, &cmdopt_none, PS_CMP_NORMAL }, { "signal", "signals", "[-n]", &procstat_sigs, &cmdopt_signals, PS_CMP_PLURAL | PS_CMP_SUBSTR }, { "thread", "threads", NULL, &procstat_threads, &cmdopt_none, PS_CMP_PLURAL }, { "tsignal", "thread_signals", "[-n]", &procstat_threads_sigs, &cmdopt_signals, PS_CMP_PLURAL | PS_CMP_SUBSTR }, { "vm", "vm", NULL, &procstat_vm, &cmdopt_none, PS_CMP_NORMAL } }; static void usage(const struct procstat_cmd *cmd) { size_t i, l; int multi; if (cmd == NULL || (cmd->cmp & PS_MODE_COMPAT) == 0) { xo_error("usage: procstat [--libxo] [-h] [-M core] [-N system]" " [-w interval] command\n" " [pid ... | core ...]\n" " procstat [--libxo] -a [-h] [-M core] [-N system] " " [-w interval] command\n" " procstat [--libxo] [-h] [-M core] [-N system]" " [-w interval]\n" " [-S | -b | -c | -e | -f [-C] | -i [-n] | " "-j [-n] | -k [-k] |\n" " -l | -r [-H] | -s | -t | -v | -x] " "[pid ... | core ...]\n" " procstat [--libxo] -a [-h] [-M core] [-N system]" " [-w interval]\n" " [-S | -b | -c | -e | -f [-C] | -i [-n] | " "-j [-n] | -k [-k] |\n" " -l | -r [-H] | -s | -t | -v | -x]\n" " procstat [--libxo] -L [-h] [-M core] [-N system] core ...\n" "Available commands:\n"); for (i = 0, l = nitems(cmd_table); i < l; i++) { multi = i + 1 < l && cmd_table[i].cmd == cmd_table[i + 1].cmd; xo_error(" %s%s%s", multi ? "[" : "", cmd_table[i].command, (cmd_table[i].cmp & PS_CMP_PLURAL) ? "(s)" : ""); for (; i + 1 < l && cmd_table[i].cmd == cmd_table[i + 1].cmd; i++) xo_error(" | %s%s", cmd_table[i + 1].command, (cmd_table[i].cmp & PS_CMP_PLURAL) ? "(s)" : ""); if (multi) xo_error("]"); if (cmd_table[i].usage != NULL) xo_error(" %s", cmd_table[i].usage); xo_error("\n"); } } else { xo_error("usage: %s [--libxo] pid ...\n", progname); } xo_finish(); exit(EX_USAGE); } static void procstat(const struct procstat_cmd *cmd, struct procstat *prstat, struct kinfo_proc *kipp) { char *pidstr = NULL; asprintf(&pidstr, "%d", kipp->ki_pid); if (pidstr == NULL) xo_errc(1, ENOMEM, "Failed to allocate memory in procstat()"); xo_open_container(pidstr); cmd->cmd(prstat, kipp); xo_close_container(pidstr); free(pidstr); } /* * Sort processes first by pid and then tid. */ static int kinfo_proc_compare(const void *a, const void *b) { int i; i = ((const struct kinfo_proc *)a)->ki_pid - ((const struct kinfo_proc *)b)->ki_pid; if (i != 0) return (i); i = ((const struct kinfo_proc *)a)->ki_tid - ((const struct kinfo_proc *)b)->ki_tid; return (i); } void kinfo_proc_sort(struct kinfo_proc *kipp, int count) { qsort(kipp, count, sizeof(*kipp), kinfo_proc_compare); } const char * kinfo_proc_thread_name(const struct kinfo_proc *kipp) { static char name[MAXCOMLEN+1]; strlcpy(name, kipp->ki_tdname, sizeof(name)); strlcat(name, kipp->ki_moretdname, sizeof(name)); if (name[0] == '\0' || strcmp(kipp->ki_comm, name) == 0) { name[0] = '-'; name[1] = '\0'; } return (name); } static const struct procstat_cmd * getcmdbyprogname(const char *pprogname) { const char *ca; size_t i; if (pprogname == NULL) return (NULL); for (i = 0; i < nitems(pacmd_table); i++) { ca = pacmd_table[i].command; if (ca != NULL && strcmp(ca, pprogname) == 0) return (&pacmd_table[i]); } return (NULL); } static const struct procstat_cmd * getcmd(const char *str) { const struct procstat_cmd *cmd; size_t i, l; int cmp, s; if (str == NULL) return (NULL); cmd = NULL; if ((l = strlen(str)) == 0) return (getcmd("basic")); s = l > 1 && strcasecmp(str + l - 1, "s") == 0; for (i = 0; i < nitems(cmd_table); i++) { /* * After the first match substring matches are disabled, * allowing subsequent full matches to take precedence. */ if (cmd == NULL && (cmd_table[i].cmp & PS_CMP_SUBSTR)) cmp = strncasecmp(str, cmd_table[i].command, l - ((cmd_table[i].cmp & PS_CMP_PLURAL) && s ? 1 : 0)); else if ((cmd_table[i].cmp & PS_CMP_PLURAL) && s && l == strlen(cmd_table[i].command) + 1) cmp = strncasecmp(str, cmd_table[i].command, l - 1); else cmp = strcasecmp(str, cmd_table[i].command); if (cmp == 0) cmd = &cmd_table[i]; } return (cmd); } int main(int argc, char *argv[]) { struct kinfo_proc *p; const struct procstat_cmd *cmd; struct procstat *prstat, *cprstat; char *dummy, *nlistf, *memf; const char *xocontainer; long l; pid_t pid; int aflag, ch, cnt, i, interval; interval = 0; cmd = NULL; memf = nlistf = NULL; aflag = 0; argc = xo_parse_args(argc, argv); progname = getprogname(); cmd = getcmdbyprogname(progname); while ((ch = getopt(argc, argv, "abCcefHhijkLlM:N:nrSstvw:x")) != -1) { switch (ch) { case 'a': aflag++; break; case 'b': if (cmd != NULL) usage(cmd); cmd = getcmd("binary"); break; case 'C': procstat_opts |= PS_OPT_CAPABILITIES; break; case 'c': if (cmd != NULL) usage(cmd); cmd = getcmd("arguments"); break; case 'e': if (cmd != NULL) usage(cmd); cmd = getcmd("environment"); break; case 'f': if (cmd != NULL) usage(cmd); cmd = getcmd("files"); break; case 'H': procstat_opts |= PS_OPT_PERTHREAD; break; case 'h': procstat_opts |= PS_OPT_NOHEADER; break; case 'i': if (cmd != NULL) usage(cmd); cmd = getcmd("signals"); break; case 'j': if (cmd != NULL) usage(cmd); cmd = getcmd("tsignals"); break; case 'k': if (cmd != NULL && cmd->cmd == procstat_kstack) { if ((procstat_opts & PS_OPT_VERBOSE) != 0) usage(cmd); procstat_opts |= PS_OPT_VERBOSE; } else { if (cmd != NULL) usage(cmd); cmd = getcmd("kstack"); } break; case 'L': if (cmd != NULL) usage(cmd); cmd = getcmd("ptlwpinfo"); break; case 'l': if (cmd != NULL) usage(cmd); cmd = getcmd("rlimit"); break; case 'M': memf = optarg; break; case 'N': nlistf = optarg; break; case 'n': procstat_opts |= PS_OPT_SIGNUM; break; case 'r': if (cmd != NULL) usage(cmd); cmd = getcmd("rusage"); break; case 'S': if (cmd != NULL) usage(cmd); cmd = getcmd("cpuset"); break; case 's': if (cmd != NULL) usage(cmd); cmd = getcmd("credentials"); break; case 't': if (cmd != NULL) usage(cmd); cmd = getcmd("threads"); break; case 'v': if (cmd != NULL) usage(cmd); cmd = getcmd("vm"); break; case 'w': l = strtol(optarg, &dummy, 10); if (*dummy != '\0') usage(cmd); if (l < 1 || l > INT_MAX) usage(cmd); interval = l; break; case 'x': if (cmd != NULL) usage(cmd); cmd = getcmd("auxv"); break; case '?': default: usage(cmd); } } argc -= optind; argv += optind; if (cmd == NULL && argv[0] != NULL && (cmd = getcmd(argv[0])) != NULL) { if ((procstat_opts & PS_SUBCOMMAND_OPTS) != 0) usage(cmd); if (cmd->opt != NULL) { optreset = 1; optind = 1; cmd->opt(argc, argv); if ((cmd->cmp & PS_MODE_COMPAT) == 0) { argc -= optind; argv += optind; } } else { argc -= 1; argv += 1; } } else { if (cmd == NULL) cmd = getcmd("basic"); if (cmd->cmd != procstat_files && (procstat_opts & PS_OPT_CAPABILITIES) != 0 && (cmd->cmp & PS_MODE_COMPAT) == 0) usage(cmd); } /* Must specify either the -a flag or a list of pids. */ if (!(aflag == 1 && argc == 0) && !(aflag == 0 && argc > 0) && (cmd->cmp & PS_MODE_NO_KINFO_PROC) == 0) usage(cmd); if (memf != NULL) prstat = procstat_open_kvm(nlistf, memf); else prstat = procstat_open_sysctl(); if (prstat == NULL) xo_errx(1, "procstat_open()"); do { xocontainer = cmd->xocontainer != NULL ? cmd->xocontainer : cmd->command; xo_set_version(PROCSTAT_XO_VERSION); xo_open_container(progname); xo_open_container(xocontainer); if ((cmd->cmp & PS_MODE_NO_KINFO_PROC) != 0) { cmd->cmd(prstat, NULL); goto iter; } if (aflag) { p = procstat_getprocs(prstat, KERN_PROC_PROC, 0, &cnt); if (p == NULL) xo_errx(1, "procstat_getprocs()"); kinfo_proc_sort(p, cnt); for (i = 0; i < cnt; i++) { procstat(cmd, prstat, &p[i]); /* Suppress header after first process. */ procstat_opts |= PS_OPT_NOHEADER; xo_flush(); } procstat_freeprocs(prstat, p); } for (i = 0; i < argc; i++) { l = strtol(argv[i], &dummy, 10); if (*dummy == '\0') { if (l < 0) usage(cmd); pid = l; p = procstat_getprocs(prstat, KERN_PROC_PID, pid, &cnt); if (p == NULL) xo_errx(1, "procstat_getprocs()"); if (cnt != 0) procstat(cmd, prstat, p); procstat_freeprocs(prstat, p); } else { if ((cmd->cmp & PS_MODE_COMPAT) == 0) { cprstat = procstat_open_core(argv[i]); if (cprstat == NULL) { warnx("procstat_open()"); continue; } p = procstat_getprocs(cprstat, KERN_PROC_PID, -1, &cnt); if (p == NULL) { xo_errx(1, "procstat_getprocs()"); } if (cnt != 0) procstat(cmd, cprstat, p); procstat_freeprocs(cprstat, p); procstat_close(cprstat); } else { usage(cmd); } } if ((cmd->cmp & PS_MODE_COMPAT) == 0) { /* Suppress header after first process. */ procstat_opts |= PS_OPT_NOHEADER; } } iter: xo_close_container(xocontainer); xo_close_container(progname); xo_finish(); if (interval) sleep(interval); } while (interval); procstat_close(prstat); exit(0); } void cmdopt_none(int argc, char * const argv[]) { int ch; while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { case '?': default: usage(NULL); } } } void cmdopt_verbose(int argc, char * const argv[]) { int ch; while ((ch = getopt(argc, argv, "v")) != -1) { switch (ch) { case 'v': procstat_opts |= PS_OPT_VERBOSE; break; case '?': default: usage(NULL); } } } void cmdopt_signals(int argc, char * const argv[]) { int ch; while ((ch = getopt(argc, argv, "n")) != -1) { switch (ch) { case 'n': procstat_opts |= PS_OPT_SIGNUM; break; case '?': default: usage(NULL); } } } void cmdopt_rusage(int argc, char * const argv[]) { int ch; while ((ch = getopt(argc, argv, "Ht")) != -1) { switch (ch) { case 'H': /* FALLTHROUGH */ case 't': procstat_opts |= PS_OPT_PERTHREAD; break; case '?': default: usage(NULL); } } } void cmdopt_files(int argc, char * const argv[]) { int ch; while ((ch = getopt(argc, argv, "C")) != -1) { switch (ch) { case 'C': procstat_opts |= PS_OPT_CAPABILITIES; break; case '?': default: usage(NULL); } } } void cmdopt_cpuset(int argc, char * const argv[]) { procstat_opts |= PS_OPT_PERTHREAD; cmdopt_none(argc, argv); } diff --git a/usr.bin/procstat/procstat_args.c b/usr.bin/procstat/procstat_args.c index c618bc37a7ca..2542fe06d487 100644 --- a/usr.bin/procstat/procstat_args.c +++ b/usr.bin/procstat/procstat_args.c @@ -1,97 +1,96 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2007 Robert N. M. Watson * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include "procstat.h" void procstat_args(struct procstat *procstat, struct kinfo_proc *kipp) { int i; char **args; if ((procstat_opts & PS_OPT_NOHEADER) == 0) { xo_emit("{T:/%5s %-16s %-53s}\n", "PID", "COMM", "ARGS"); } args = procstat_getargv(procstat, kipp, 0); xo_emit("{k:process_id/%5d/%d} {:command/%-16s/%s}", kipp->ki_pid, kipp->ki_comm); if (args == NULL) { xo_emit(" {d:args/-}\n"); return; } xo_open_list("arguments"); for (i = 0; args[i] != NULL; i++) xo_emit(" {l:args/%s}", args[i]); xo_close_list("arguments"); xo_emit("\n"); } void procstat_env(struct procstat *procstat, struct kinfo_proc *kipp) { int i; char **envs; if ((procstat_opts & PS_OPT_NOHEADER) == 0) { xo_emit("{T:/%5s %-16s %-53s}\n", "PID", "COMM", "ENVIRONMENT"); } envs = procstat_getenvv(procstat, kipp, 0); xo_emit("{k:process_id/%5d/%d} {:command/%-16s/%s}", kipp->ki_pid, kipp->ki_comm); if (envs == NULL) { xo_emit(" {d:env/-}\n"); return; } xo_open_list("environment"); for (i = 0; envs[i] != NULL; i++) xo_emit(" {l:env/%s}", envs[i]); xo_close_list("environment"); xo_emit("\n"); } diff --git a/usr.bin/procstat/procstat_auxv.c b/usr.bin/procstat/procstat_auxv.c index 7b68ac72f724..b37e27c65161 100644 --- a/usr.bin/procstat/procstat_auxv.c +++ b/usr.bin/procstat/procstat_auxv.c @@ -1,270 +1,269 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2011 Mikolaj Golub * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include "procstat.h" void procstat_auxv(struct procstat *procstat, struct kinfo_proc *kipp) { Elf_Auxinfo *auxv; u_int count, i; static char prefix[256]; if ((procstat_opts & PS_OPT_NOHEADER) == 0) xo_emit("{T:/%5s %-19s %-16s %-16s}\n", "PID", "COMM", "AUXV", "VALUE"); auxv = procstat_getauxv(procstat, kipp, &count); if (auxv == NULL) return; snprintf(prefix, sizeof(prefix), "%5d %-19s", kipp->ki_pid, kipp->ki_comm); xo_emit("{e:process_id/%5d/%d}{e:command/%-19s/%s}", kipp->ki_pid, kipp->ki_comm); for (i = 0; i < count; i++) { switch(auxv[i].a_type) { case AT_NULL: return; case AT_IGNORE: break; case AT_EXECFD: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_EXECFD/%ld}\n", prefix, "AT_EXECFD", (long)auxv[i].a_un.a_val); break; case AT_PHDR: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_PHDR/%p}\n", prefix, "AT_PHDR", auxv[i].a_un.a_ptr); break; case AT_PHENT: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_PHENT/%ld}\n", prefix, "AT_PHENT", (long)auxv[i].a_un.a_val); break; case AT_PHNUM: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_PHNUM/%ld}\n", prefix, "AT_PHNUM", (long)auxv[i].a_un.a_val); break; case AT_PAGESZ: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_PAGESZ/%ld}\n", prefix, "AT_PAGESZ", (long)auxv[i].a_un.a_val); break; case AT_BASE: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_BASE/%p}\n", prefix, "AT_BASE", auxv[i].a_un.a_ptr); break; case AT_FLAGS: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_FLAGS/%#lx}\n", prefix, "AT_FLAGS", (u_long)auxv[i].a_un.a_val); break; case AT_ENTRY: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_ENTRY/%p}\n", prefix, "AT_ENTRY", auxv[i].a_un.a_ptr); break; #ifdef AT_NOTELF case AT_NOTELF: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_NOTELF/%ld}\n", prefix, "AT_NOTELF", (long)auxv[i].a_un.a_val); break; #endif #ifdef AT_UID case AT_UID: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_UID/%ld}\n", prefix, "AT_UID", (long)auxv[i].a_un.a_val); break; #endif #ifdef AT_EUID case AT_EUID: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_EUID/%ld}\n", prefix, "AT_EUID", (long)auxv[i].a_un.a_val); break; #endif #ifdef AT_GID case AT_GID: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_GID/%ld}\n", prefix, "AT_GID", (long)auxv[i].a_un.a_val); break; #endif #ifdef AT_EGID case AT_EGID: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_EGID/%ld}\n", prefix, "AT_EGID", (long)auxv[i].a_un.a_val); break; #endif case AT_EXECPATH: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_EXECPATH/%p}\n", prefix, "AT_EXECPATH", auxv[i].a_un.a_ptr); break; case AT_CANARY: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_CANARY/%p}\n", prefix, "AT_CANARY", auxv[i].a_un.a_ptr); break; case AT_CANARYLEN: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_CANARYLEN/%ld}\n", prefix, "AT_CANARYLEN", (long)auxv[i].a_un.a_val); break; case AT_OSRELDATE: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_OSRELDATE/%ld}\n", prefix, "AT_OSRELDATE", (long)auxv[i].a_un.a_val); break; case AT_NCPUS: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_NCPUS/%ld}\n", prefix, "AT_NCPUS", (long)auxv[i].a_un.a_val); break; case AT_PAGESIZES: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_PAGESIZES/%p}\n", prefix, "AT_PAGESIZES", auxv[i].a_un.a_ptr); break; case AT_PAGESIZESLEN: xo_emit("{dw:/%s}{Lw:/%-16s/%s}" "{:AT_PAGESIZESLEN/%ld}\n", prefix, "AT_PAGESIZESLEN", (long)auxv[i].a_un.a_val); break; case AT_STACKPROT: if ((auxv[i].a_un.a_val & VM_PROT_EXECUTE) != 0) xo_emit("{dw:/%s}{Lw:/%-16s/%s}" "{:AT_STACKPROT/%s}\n", prefix, "AT_STACKPROT", "EXECUTABLE"); else xo_emit("{dw:/%s}{Lw:/%-16s/%s}" "{:AT_STACKPROT/%s}\n", prefix, "AT_STACKPROT", "NONEXECUTABLE"); break; #ifdef AT_TIMEKEEP case AT_TIMEKEEP: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_TIMEKEEP/%p}\n", prefix, "AT_TIMEKEEP", auxv[i].a_un.a_ptr); break; #endif #ifdef AT_EHDRFLAGS case AT_EHDRFLAGS: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_EHDRFLAGS/%#lx}\n", prefix, "AT_EHDRFLAGS", (u_long)auxv[i].a_un.a_val); break; #endif #ifdef AT_HWCAP case AT_HWCAP: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_HWCAP/%#lx}\n", prefix, "AT_HWCAP", (u_long)auxv[i].a_un.a_val); break; #endif #ifdef AT_HWCAP2 case AT_HWCAP2: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_HWCAP2/%#lx}\n", prefix, "AT_HWCAP2", (u_long)auxv[i].a_un.a_val); break; #endif #ifdef AT_BSDFLAGS case AT_BSDFLAGS: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_BSDFLAGS/%#lx}\n", prefix, "AT_BSDFLAGS", (u_long)auxv[i].a_un.a_val); break; #endif #ifdef AT_ARGC case AT_ARGC: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_ARGC/%ld}\n", prefix, "AT_ARGC", (long)auxv[i].a_un.a_val); break; #endif #ifdef AT_ARGV case AT_ARGV: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_ARGV/%p}\n", prefix, "AT_ARGV", auxv[i].a_un.a_ptr); break; #endif #ifdef AT_ENVC case AT_ENVC: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_ENVC/%ld}\n", prefix, "AT_ENVC", (long)auxv[i].a_un.a_val); break; #endif #ifdef AT_ENVV case AT_ENVV: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_ENVV/%p}\n", prefix, "AT_ENVV", auxv[i].a_un.a_ptr); break; #endif #ifdef AT_PS_STRINGS case AT_PS_STRINGS: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_PS_STRINGS/%p}\n", prefix, "AT_PS_STRINGS", auxv[i].a_un.a_ptr); break; #endif #ifdef AT_FXRNG case AT_FXRNG: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_FXRNG/%p}\n", prefix, "AT_FXRNG", auxv[i].a_un.a_ptr); break; #endif #ifdef AT_KPRELOAD case AT_KPRELOAD: xo_emit("{dw:/%s}{Lw:/%-16s/%s}{:AT_KPRELOAD/%p}\n", prefix, "AT_KPRELOAD", auxv[i].a_un.a_ptr); break; #endif #ifdef AT_USRSTACKBASE case AT_USRSTACKBASE: xo_emit("{dw:/%s}{Lw:/%-16s/%s}" "{:AT_USRSTACKBASE/%#lx}\n", prefix, "AT_USRSTACKBASE", auxv[i].a_un.a_val); break; #endif #ifdef AT_USRSTACKLIM case AT_USRSTACKLIM: xo_emit("{dw:/%s}{Lw:/%-16s/%s}" "{:AT_USRSTACKLIM/%#lx}\n", prefix, "AT_USRSTACKLIM", auxv[i].a_un.a_val); break; #endif default: xo_emit("{dw:/%s}{Lw:/%16ld/%ld}{:UNKNOWN/%#lx}\n", prefix, auxv[i].a_type, auxv[i].a_un.a_val); break; } } xo_emit("\n"); procstat_freeauxv(procstat, auxv); } diff --git a/usr.bin/procstat/procstat_basic.c b/usr.bin/procstat/procstat_basic.c index 082689b6bd84..e1e3e363a9f1 100644 --- a/usr.bin/procstat/procstat_basic.c +++ b/usr.bin/procstat/procstat_basic.c @@ -1,69 +1,68 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2007 Robert N. M. Watson * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include "procstat.h" void procstat_basic(struct procstat *procstat __unused, struct kinfo_proc *kipp) { if ((procstat_opts & PS_OPT_NOHEADER) == 0) xo_emit("{T:/%5s %5s %5s %5s %5s %3s %-8s %-9s %-13s %-12s}\n", "PID", "PPID", "PGID", "SID", "TSID", "THR", "LOGIN", "WCHAN", "EMUL", "COMM"); xo_emit("{k:process_id/%5d/%d} ", kipp->ki_pid); xo_emit("{:parent_process_id/%5d/%d} ", kipp->ki_ppid); xo_emit("{:process_group_id/%5d/%d} ", kipp->ki_pgid); xo_emit("{:session_id/%5d/%d} ", kipp->ki_sid); xo_emit("{:terminal_session_id/%5d/%d} ", kipp->ki_tsid); xo_emit("{:threads/%3d/%d} ", kipp->ki_numthreads); xo_emit("{:login/%-8s/%s} ", strlen(kipp->ki_login) ? kipp->ki_login : "-"); if (kipp->ki_kiflag & KI_LOCKBLOCK) { xo_emit("{:lockname/*%-8s/%s} ", strlen(kipp->ki_lockname) ? kipp->ki_lockname : "-"); } else { xo_emit("{:wait_channel/%-9s/%s} ", strlen(kipp->ki_wmesg) ? kipp->ki_wmesg : "-"); } xo_emit("{:emulation/%-13s/%s} ", strcmp(kipp->ki_emul, "null") ? kipp->ki_emul : "-"); xo_emit("{:command/%-12s/%s}\n", kipp->ki_comm); } diff --git a/usr.bin/procstat/procstat_bin.c b/usr.bin/procstat/procstat_bin.c index a0e272808f1c..04482e5ee1ba 100644 --- a/usr.bin/procstat/procstat_bin.c +++ b/usr.bin/procstat/procstat_bin.c @@ -1,65 +1,64 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2007 Robert N. M. Watson * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include "procstat.h" void procstat_bin(struct procstat *prstat, struct kinfo_proc *kipp) { int osrel; static char pathname[PATH_MAX]; if ((procstat_opts & PS_OPT_NOHEADER) == 0) xo_emit("{T:/%5s %-16s %8s %s}\n", "PID", "COMM", "OSREL", "PATH"); if (procstat_getpathname(prstat, kipp, pathname, sizeof(pathname)) != 0) return; if (strlen(pathname) == 0) strcpy(pathname, "-"); if (procstat_getosrel(prstat, kipp, &osrel) != 0) return; xo_emit("{k:process_id/%5d/%d} ", kipp->ki_pid); xo_emit("{:command/%-16s/%s} ", kipp->ki_comm); xo_emit("{:osrel/%8d/%d} ", osrel); xo_emit("{:pathname/%s}\n", pathname); } diff --git a/usr.bin/procstat/procstat_cred.c b/usr.bin/procstat/procstat_cred.c index f37a328e362f..7f1efeab9d4c 100644 --- a/usr.bin/procstat/procstat_cred.c +++ b/usr.bin/procstat/procstat_cred.c @@ -1,104 +1,103 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2007-2008 Robert N. M. Watson * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include "procstat.h" static const char *get_umask(struct procstat *procstat, struct kinfo_proc *kipp); void procstat_cred(struct procstat *procstat, struct kinfo_proc *kipp) { unsigned int i, ngroups; gid_t *groups; if ((procstat_opts & PS_OPT_NOHEADER) == 0) xo_emit("{T:/%5s %-16s %5s %5s %5s %5s %5s %5s %5s %5s %-15s}\n", "PID", "COMM", "EUID", "RUID", "SVUID", "EGID", "RGID", "SVGID", "UMASK", "FLAGS", "GROUPS"); xo_emit("{k:process_id/%5d/%d} ", kipp->ki_pid); xo_emit("{:command/%-16s/%s} ", kipp->ki_comm); xo_emit("{:uid/%5d} ", kipp->ki_uid); xo_emit("{:ruid/%5d} ", kipp->ki_ruid); xo_emit("{:svuid/%5d} ", kipp->ki_svuid); xo_emit("{:group/%5d} ", kipp->ki_groups[0]); xo_emit("{:rgid/%5d} ", kipp->ki_rgid); xo_emit("{:svgid/%5d} ", kipp->ki_svgid); xo_emit("{:umask/%5s} ", get_umask(procstat, kipp)); xo_emit("{:cr_flags/%s}", kipp->ki_cr_flags & CRED_FLAG_CAPMODE ? "C" : "-"); xo_emit("{P: }"); groups = NULL; /* * We may have too many groups to fit in kinfo_proc's statically * sized storage. If that occurs, attempt to retrieve them using * libprocstat. */ if (kipp->ki_cr_flags & KI_CRF_GRP_OVERFLOW) groups = procstat_getgroups(procstat, kipp, &ngroups); if (groups == NULL) { ngroups = kipp->ki_ngroups; groups = kipp->ki_groups; } xo_open_list("groups"); for (i = 0; i < ngroups; i++) xo_emit("{D:/%s}{l:groups/%d}", (i > 0) ? "," : "", groups[i]); if (groups != kipp->ki_groups) procstat_freegroups(procstat, groups); xo_close_list("groups"); xo_emit("\n"); } static const char * get_umask(struct procstat *procstat, struct kinfo_proc *kipp) { u_short fd_cmask; static char umask[4]; if (procstat_getumask(procstat, kipp, &fd_cmask) == 0) { snprintf(umask, 4, "%03o", fd_cmask); return (umask); } else { return ("-"); } } diff --git a/usr.bin/procstat/procstat_cs.c b/usr.bin/procstat/procstat_cs.c index 89da29c598c2..39d4174cf06a 100644 --- a/usr.bin/procstat/procstat_cs.c +++ b/usr.bin/procstat/procstat_cs.c @@ -1,117 +1,116 @@ /*- * Copyright (c) 2007 Robert N. M. Watson * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include "procstat.h" void procstat_cs(struct procstat *procstat, struct kinfo_proc *kipp) { cpusetid_t cs; cpuset_t mask; struct kinfo_proc *kip; struct sbuf *cpusetbuf; unsigned int count, i; int once, twice, lastcpu, cpu; if ((procstat_opts & PS_OPT_NOHEADER) == 0) xo_emit("{T:/%5s %6s %-19s %-19s %2s %4s %-7s}\n", "PID", "TID", "COMM", "TDNAME", "CPU", "CSID", "CPU MASK"); kip = procstat_getprocs(procstat, KERN_PROC_PID | KERN_PROC_INC_THREAD, kipp->ki_pid, &count); if (kip == NULL) return; kinfo_proc_sort(kip, count); for (i = 0; i < count; i++) { kipp = &kip[i]; xo_emit("{k:process_id/%5d/%d} ", kipp->ki_pid); xo_emit("{:thread_id/%6d/%d} ", kipp->ki_tid); xo_emit("{:command/%-19s/%s} ", strlen(kipp->ki_comm) ? kipp->ki_comm : "-"); xo_emit("{:thread_name/%-19s/%s} ", kinfo_proc_thread_name(kipp)); if (kipp->ki_oncpu != 255) xo_emit("{:cpu/%3d/%d} ", kipp->ki_oncpu); else if (kipp->ki_lastcpu != 255) xo_emit("{:cpu/%3d/%d} ", kipp->ki_lastcpu); else xo_emit("{:cpu/%3s/%s} ", "-"); if (cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, kipp->ki_tid, &cs) != 0) { cs = CPUSET_INVALID; } xo_emit("{:cpu_set_id/%4d/%d} ", cs); if ((cs != CPUSET_INVALID) && (cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, kipp->ki_tid, sizeof(mask), &mask) == 0)) { lastcpu = -1; once = 0; twice = 0; cpusetbuf = sbuf_new_auto(); for (cpu = 0; cpu < CPU_SETSIZE; cpu++) { if (CPU_ISSET(cpu, &mask)) { if (once == 0) { sbuf_printf(cpusetbuf, "%d", cpu); once = 1; } else if (cpu == lastcpu + 1) { twice = 1; } else if (twice == 1) { sbuf_printf(cpusetbuf, "-%d,%d", lastcpu, cpu); twice = 0; } else sbuf_printf(cpusetbuf, ",%d", cpu); lastcpu = cpu; } } if (once && twice) sbuf_printf(cpusetbuf, "-%d", lastcpu); if (sbuf_finish(cpusetbuf) != 0) xo_err(1, "Could not generate output"); xo_emit("{:cpu_set/%s}", sbuf_data(cpusetbuf)); sbuf_delete(cpusetbuf); } xo_emit("\n"); } procstat_freeprocs(procstat, kip); } diff --git a/usr.bin/procstat/procstat_files.c b/usr.bin/procstat/procstat_files.c index 99a0580f50d4..359c2d13c86d 100644 --- a/usr.bin/procstat/procstat_files.c +++ b/usr.bin/procstat/procstat_files.c @@ -1,595 +1,594 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2007-2011 Robert N. M. Watson * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "procstat.h" static const char * protocol_to_string(int domain, int type, int protocol) { switch (domain) { case AF_INET: case AF_INET6: switch (protocol) { case IPPROTO_TCP: return ("TCP"); case IPPROTO_UDP: return ("UDP"); case IPPROTO_ICMP: return ("ICM"); case IPPROTO_RAW: return ("RAW"); case IPPROTO_SCTP: return ("SCT"); default: return ("IP?"); } case AF_LOCAL: switch (type) { case SOCK_STREAM: return ("UDS"); case SOCK_DGRAM: return ("UDD"); default: return ("UD?"); } case AF_DIVERT: return ("IPD"); break; default: return ("?"); } } static void addr_to_string(struct sockaddr_storage *ss, char *buffer, int buflen) { char buffer2[INET6_ADDRSTRLEN]; struct sockaddr_in6 *sin6; struct sockaddr_in *sin; struct sockaddr_un *sun; switch (ss->ss_family) { case AF_LOCAL: sun = (struct sockaddr_un *)ss; if (strlen(sun->sun_path) == 0) strlcpy(buffer, "-", buflen); else strlcpy(buffer, sun->sun_path, buflen); break; case AF_INET: sin = (struct sockaddr_in *)ss; if (sin->sin_addr.s_addr == INADDR_ANY) snprintf(buffer, buflen, "%s:%d", "*", ntohs(sin->sin_port)); else if (inet_ntop(AF_INET, &sin->sin_addr, buffer2, sizeof(buffer2)) != NULL) snprintf(buffer, buflen, "%s:%d", buffer2, ntohs(sin->sin_port)); break; case AF_INET6: sin6 = (struct sockaddr_in6 *)ss; if (inet_ntop(AF_INET6, &sin6->sin6_addr, buffer2, sizeof(buffer2)) != NULL) snprintf(buffer, buflen, "%s.%d", buffer2, ntohs(sin6->sin6_port)); else strlcpy(buffer, "-", buflen); break; default: strlcpy(buffer, "", buflen); break; } } static struct cap_desc { uint64_t cd_right; const char *cd_desc; } cap_desc[] = { /* General file I/O. */ { CAP_READ, "rd" }, { CAP_WRITE, "wr" }, { CAP_SEEK, "se" }, { CAP_MMAP, "mm" }, { CAP_CREATE, "cr" }, { CAP_FEXECVE, "fe" }, { CAP_FSYNC, "fy" }, { CAP_FTRUNCATE, "ft" }, /* VFS methods. */ { CAP_FCHDIR, "cd" }, { CAP_FCHFLAGS, "cf" }, { CAP_FCHMOD, "cm" }, { CAP_FCHOWN, "cn" }, { CAP_FCNTL, "fc" }, { CAP_FLOCK, "fl" }, { CAP_FPATHCONF, "fp" }, { CAP_FSCK, "fk" }, { CAP_FSTAT, "fs" }, { CAP_FSTATFS, "sf" }, { CAP_FUTIMES, "fu" }, { CAP_LINKAT_SOURCE, "ls" }, { CAP_LINKAT_TARGET, "lt" }, { CAP_MKDIRAT, "md" }, { CAP_MKFIFOAT, "mf" }, { CAP_MKNODAT, "mn" }, { CAP_RENAMEAT_SOURCE, "rs" }, { CAP_RENAMEAT_TARGET, "rt" }, { CAP_SYMLINKAT, "sl" }, { CAP_UNLINKAT, "un" }, /* Lookups - used to constrain *at() calls. */ { CAP_LOOKUP, "lo" }, /* Extended attributes. */ { CAP_EXTATTR_GET, "eg" }, { CAP_EXTATTR_SET, "es" }, { CAP_EXTATTR_DELETE, "ed" }, { CAP_EXTATTR_LIST, "el" }, /* Access Control Lists. */ { CAP_ACL_GET, "ag" }, { CAP_ACL_SET, "as" }, { CAP_ACL_DELETE, "ad" }, { CAP_ACL_CHECK, "ac" }, /* Socket operations. */ { CAP_ACCEPT, "at" }, { CAP_BIND, "bd" }, { CAP_CONNECT, "co" }, { CAP_GETPEERNAME, "pn" }, { CAP_GETSOCKNAME, "sn" }, { CAP_GETSOCKOPT, "gs" }, { CAP_LISTEN, "ln" }, { CAP_PEELOFF, "pf" }, { CAP_SETSOCKOPT, "ss" }, { CAP_SHUTDOWN, "sh" }, /* Mandatory Access Control. */ { CAP_MAC_GET, "mg" }, { CAP_MAC_SET, "ms" }, /* Methods on semaphores. */ { CAP_SEM_GETVALUE, "sg" }, { CAP_SEM_POST, "sp" }, { CAP_SEM_WAIT, "sw" }, /* Event monitoring and posting. */ { CAP_EVENT, "ev" }, { CAP_KQUEUE_EVENT, "ke" }, { CAP_KQUEUE_CHANGE, "kc" }, /* Strange and powerful rights that should not be given lightly. */ { CAP_IOCTL, "io" }, { CAP_TTYHOOK, "ty" }, /* Process management via process descriptors. */ { CAP_PDGETPID, "pg" }, { CAP_PDWAIT, "pw" }, { CAP_PDKILL, "pk" }, /* * Rights that allow to use bindat(2) and connectat(2) syscalls on a * directory descriptor. */ { CAP_BINDAT, "ba" }, { CAP_CONNECTAT, "ca" }, /* Aliases and defines that combine multiple rights. */ { CAP_PREAD, "prd" }, { CAP_PWRITE, "pwr" }, { CAP_MMAP_R, "mmr" }, { CAP_MMAP_W, "mmw" }, { CAP_MMAP_X, "mmx" }, { CAP_MMAP_RW, "mrw" }, { CAP_MMAP_RX, "mrx" }, { CAP_MMAP_WX, "mwx" }, { CAP_MMAP_RWX, "mma" }, { CAP_RECV, "re" }, { CAP_SEND, "sd" }, { CAP_SOCK_CLIENT, "scl" }, { CAP_SOCK_SERVER, "ssr" }, }; static const u_int cap_desc_count = nitems(cap_desc); static u_int width_capability(cap_rights_t *rightsp) { u_int count, i, width; count = 0; width = 0; for (i = 0; i < cap_desc_count; i++) { if (cap_rights_is_set(rightsp, cap_desc[i].cd_right)) { width += strlen(cap_desc[i].cd_desc); if (count) width++; count++; } } return (width); } static void print_capability(cap_rights_t *rightsp, u_int capwidth) { u_int count, i; count = 0; for (i = width_capability(rightsp); i < capwidth; i++) { if (i != 0) xo_emit(" "); else xo_emit("-"); } xo_open_list("capabilities"); for (i = 0; i < cap_desc_count; i++) { if (cap_rights_is_set(rightsp, cap_desc[i].cd_right)) { xo_emit("{D:/%s}{l:capabilities/%s}", count ? "," : "", cap_desc[i].cd_desc); count++; } } xo_close_list("capabilities"); } void procstat_files(struct procstat *procstat, struct kinfo_proc *kipp) { struct sockstat sock; struct filestat_list *head; struct filestat *fst; const char *str; struct vnstat vn; u_int capwidth, width; int error; char src_addr[PATH_MAX]; char dst_addr[PATH_MAX]; /* * To print the header in capability mode, we need to know the width * of the widest capability string. Even if we get no processes * back, we will print the header, so we defer aborting due to a lack * of processes until after the header logic. */ capwidth = 0; head = procstat_getfiles(procstat, kipp, 0); if (head != NULL && (procstat_opts & PS_OPT_CAPABILITIES) != 0) { STAILQ_FOREACH(fst, head, next) { width = width_capability(&fst->fs_cap_rights); if (width > capwidth) capwidth = width; } if (capwidth < strlen("CAPABILITIES")) capwidth = strlen("CAPABILITIES"); } if ((procstat_opts & PS_OPT_NOHEADER) == 0) { if ((procstat_opts & PS_OPT_CAPABILITIES) != 0) xo_emit("{T:/%5s %-16s %5s %1s %-8s %-*s " "%-3s %-12s}\n", "PID", "COMM", "FD", "T", "FLAGS", capwidth, "CAPABILITIES", "PRO", "NAME"); else xo_emit("{T:/%5s %-16s %5s %1s %1s %-8s " "%3s %7s %-3s %-12s}\n", "PID", "COMM", "FD", "T", "V", "FLAGS", "REF", "OFFSET", "PRO", "NAME"); } if (head == NULL) return; xo_emit("{ek:process_id/%5d/%d}", kipp->ki_pid); xo_emit("{e:command/%-16s/%s}", kipp->ki_comm); xo_open_list("files"); STAILQ_FOREACH(fst, head, next) { xo_open_instance("files"); xo_emit("{dk:process_id/%5d/%d} ", kipp->ki_pid); xo_emit("{d:command/%-16s/%s} ", kipp->ki_comm); if (fst->fs_uflags & PS_FST_UFLAG_CTTY) xo_emit("{P: }{:fd/%s} ", "ctty"); else if (fst->fs_uflags & PS_FST_UFLAG_CDIR) xo_emit("{P: }{:fd/%s} ", "cwd"); else if (fst->fs_uflags & PS_FST_UFLAG_JAIL) xo_emit("{P: }{:fd/%s} ", "jail"); else if (fst->fs_uflags & PS_FST_UFLAG_RDIR) xo_emit("{P: }{:fd/%s} ", "root"); else if (fst->fs_uflags & PS_FST_UFLAG_TEXT) xo_emit("{P: }{:fd/%s} ", "text"); else if (fst->fs_uflags & PS_FST_UFLAG_TRACE) xo_emit("{:fd/%s} ", "trace"); else xo_emit("{:fd/%5d} ", fst->fs_fd); switch (fst->fs_type) { case PS_FST_TYPE_VNODE: str = "v"; xo_emit("{eq:fd_type/vnode}"); break; case PS_FST_TYPE_SOCKET: str = "s"; xo_emit("{eq:fd_type/socket}"); break; case PS_FST_TYPE_PIPE: str = "p"; xo_emit("{eq:fd_type/pipe}"); break; case PS_FST_TYPE_FIFO: str = "f"; xo_emit("{eq:fd_type/fifo}"); break; case PS_FST_TYPE_KQUEUE: str = "k"; xo_emit("{eq:fd_type/kqueue}"); break; case PS_FST_TYPE_MQUEUE: str = "m"; xo_emit("{eq:fd_type/mqueue}"); break; case PS_FST_TYPE_SHM: str = "h"; xo_emit("{eq:fd_type/shm}"); break; case PS_FST_TYPE_PTS: str = "t"; xo_emit("{eq:fd_type/pts}"); break; case PS_FST_TYPE_SEM: str = "e"; xo_emit("{eq:fd_type/sem}"); break; case PS_FST_TYPE_PROCDESC: str = "P"; xo_emit("{eq:fd_type/procdesc}"); break; case PS_FST_TYPE_DEV: str = "D"; xo_emit("{eq:fd_type/dev}"); break; case PS_FST_TYPE_EVENTFD: str = "E"; xo_emit("{eq:fd_type/eventfd}"); break; case PS_FST_TYPE_NONE: str = "?"; xo_emit("{eq:fd_type/none}"); break; case PS_FST_TYPE_UNKNOWN: default: str = "?"; xo_emit("{eq:fd_type/unknown}"); break; } xo_emit("{d:fd_type/%1s/%s} ", str); if ((procstat_opts & PS_OPT_CAPABILITIES) == 0) { str = "-"; if (fst->fs_type == PS_FST_TYPE_VNODE) { error = procstat_get_vnode_info(procstat, fst, &vn, NULL); switch (vn.vn_type) { case PS_FST_VTYPE_VREG: str = "r"; xo_emit("{eq:vode_type/regular}"); break; case PS_FST_VTYPE_VDIR: str = "d"; xo_emit("{eq:vode_type/directory}"); break; case PS_FST_VTYPE_VBLK: str = "b"; xo_emit("{eq:vode_type/block}"); break; case PS_FST_VTYPE_VCHR: str = "c"; xo_emit("{eq:vode_type/character}"); break; case PS_FST_VTYPE_VLNK: str = "l"; xo_emit("{eq:vode_type/link}"); break; case PS_FST_VTYPE_VSOCK: str = "s"; xo_emit("{eq:vode_type/socket}"); break; case PS_FST_VTYPE_VFIFO: str = "f"; xo_emit("{eq:vode_type/fifo}"); break; case PS_FST_VTYPE_VBAD: str = "x"; xo_emit("{eq:vode_type/revoked_device}"); break; case PS_FST_VTYPE_VNON: str = "?"; xo_emit("{eq:vode_type/non}"); break; case PS_FST_VTYPE_UNKNOWN: default: str = "?"; xo_emit("{eq:vode_type/unknown}"); break; } } xo_emit("{d:vnode_type/%1s/%s} ", str); } xo_emit("{d:/%s}", fst->fs_fflags & PS_FST_FFLAG_READ ? "r" : "-"); xo_emit("{d:/%s}", fst->fs_fflags & PS_FST_FFLAG_WRITE ? "w" : "-"); xo_emit("{d:/%s}", fst->fs_fflags & PS_FST_FFLAG_APPEND ? "a" : "-"); xo_emit("{d:/%s}", fst->fs_fflags & PS_FST_FFLAG_ASYNC ? "s" : "-"); xo_emit("{d:/%s}", fst->fs_fflags & PS_FST_FFLAG_SYNC ? "f" : "-"); xo_emit("{d:/%s}", fst->fs_fflags & PS_FST_FFLAG_NONBLOCK ? "n" : "-"); xo_emit("{d:/%s}", fst->fs_fflags & PS_FST_FFLAG_DIRECT ? "d" : "-"); xo_emit("{d:/%s}", fst->fs_fflags & PS_FST_FFLAG_HASLOCK ? "l" : "-"); xo_emit(" "); xo_open_list("fd_flags"); if (fst->fs_fflags & PS_FST_FFLAG_READ) xo_emit("{elq:fd_flags/read}"); if (fst->fs_fflags & PS_FST_FFLAG_WRITE) xo_emit("{elq:fd_flags/write}"); if (fst->fs_fflags & PS_FST_FFLAG_APPEND) xo_emit("{elq:fd_flags/append}"); if (fst->fs_fflags & PS_FST_FFLAG_ASYNC) xo_emit("{elq:fd_flags/async}"); if (fst->fs_fflags & PS_FST_FFLAG_SYNC) xo_emit("{elq:fd_flags/fsync}"); if (fst->fs_fflags & PS_FST_FFLAG_NONBLOCK) xo_emit("{elq:fd_flags/nonblocking}"); if (fst->fs_fflags & PS_FST_FFLAG_DIRECT) xo_emit("{elq:fd_flags/direct_io}"); if (fst->fs_fflags & PS_FST_FFLAG_HASLOCK) xo_emit("{elq:fd_flags/lock_held}"); xo_close_list("fd_flags"); if ((procstat_opts & PS_OPT_CAPABILITIES) == 0) { if (fst->fs_ref_count > -1) xo_emit("{:ref_count/%3d/%d} ", fst->fs_ref_count); else xo_emit("{q:ref_count/%3c/%c} ", '-'); if (fst->fs_offset > -1) xo_emit("{:offset/%7jd/%jd} ", (intmax_t)fst->fs_offset); else xo_emit("{q:offset/%7c/%c} ", '-'); } if ((procstat_opts & PS_OPT_CAPABILITIES) != 0) { print_capability(&fst->fs_cap_rights, capwidth); xo_emit(" "); } switch (fst->fs_type) { case PS_FST_TYPE_SOCKET: error = procstat_get_socket_info(procstat, fst, &sock, NULL); if (error != 0) break; xo_emit("{:protocol/%-3s/%s} ", protocol_to_string(sock.dom_family, sock.type, sock.proto)); if (sock.proto == IPPROTO_TCP || sock.proto == IPPROTO_SCTP || sock.type == SOCK_STREAM) { xo_emit("{:sendq/%u} ", sock.sendq); xo_emit("{:recvq/%u} ", sock.recvq); } /* * While generally we like to print two addresses, * local and peer, for sockets, it turns out to be * more useful to print the first non-nul address for * local sockets, as typically they aren't bound and * connected, and the path strings can get long. */ if (sock.dom_family == AF_LOCAL) { struct sockaddr_un *sun = (struct sockaddr_un *)&sock.sa_local; if (sun->sun_path[0] != 0) addr_to_string(&sock.sa_local, src_addr, sizeof(src_addr)); else addr_to_string(&sock.sa_peer, src_addr, sizeof(src_addr)); xo_emit("{:path/%s}", src_addr); } else { addr_to_string(&sock.sa_local, src_addr, sizeof(src_addr)); addr_to_string(&sock.sa_peer, dst_addr, sizeof(dst_addr)); xo_emit("{:path/%s %s}", src_addr, dst_addr); } break; default: xo_emit("{:protocol/%-3s/%s} ", "-"); xo_emit("{:path/%-18s/%s}", fst->fs_path != NULL ? fst->fs_path : "-"); } xo_emit("\n"); xo_close_instance("files"); } xo_close_list("files"); procstat_freefiles(procstat, head); } diff --git a/usr.bin/procstat/procstat_kstack.c b/usr.bin/procstat/procstat_kstack.c index c1b7e06a64f7..49505bcb9efc 100644 --- a/usr.bin/procstat/procstat_kstack.c +++ b/usr.bin/procstat/procstat_kstack.c @@ -1,248 +1,247 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2007 Robert N. M. Watson * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include "procstat.h" /* * Walk the stack trace provided by the kernel and reduce it to what we * actually want to print. This involves stripping true instruction pointers, * frame numbers, and carriage returns as generated by stack(9). If -kk is * specified, print the function and offset, otherwise just the function. */ enum trace_state { TS_FRAMENUM, TS_PC, TS_AT, TS_FUNC, TS_OFF }; static enum trace_state kstack_nextstate(enum trace_state ts) { switch (ts) { case TS_FRAMENUM: return (TS_PC); case TS_PC: return (TS_AT); case TS_AT: return (TS_FUNC); case TS_FUNC: return (TS_OFF); case TS_OFF: return (TS_FRAMENUM); default: errx(-1, "kstack_nextstate"); } } static void kstack_cleanup(const char *old, char *new, int kflag) { enum trace_state old_ts, ts; const char *cp_old; char *cp_new; ts = TS_FRAMENUM; for (cp_old = old, cp_new = new; *cp_old != '\0'; cp_old++) { switch (*cp_old) { case ' ': case '\n': case '+': old_ts = ts; ts = kstack_nextstate(old_ts); if (old_ts == TS_OFF) { *cp_new = ' '; cp_new++; } if (kflag > 1 && old_ts == TS_FUNC) { *cp_new = '+'; cp_new++; } continue; } if (ts == TS_FUNC || (kflag > 1 && ts == TS_OFF)) { *cp_new = *cp_old; cp_new++; } } *cp_new = '\0'; } static void kstack_cleanup_encoded(const char *old, char *new, int kflag) { enum trace_state old_ts, ts; const char *cp_old; char *cp_new, *cp_loop, *cp_tofree, *cp_line; ts = TS_FRAMENUM; if (kflag == 1) { for (cp_old = old, cp_new = new; *cp_old != '\0'; cp_old++) { switch (*cp_old) { case '\n': *cp_new = *cp_old; cp_new++; case ' ': case '+': old_ts = ts; ts = kstack_nextstate(old_ts); continue; } if (ts == TS_FUNC) { *cp_new = *cp_old; cp_new++; } } *cp_new = '\0'; cp_tofree = cp_loop = strdup(new); } else cp_tofree = cp_loop = strdup(old); while ((cp_line = strsep(&cp_loop, "\n")) != NULL) { if (strlen(cp_line) != 0 && *cp_line != 127) xo_emit("{le:token/%s}", cp_line); } free(cp_tofree); } /* * Sort threads by tid. */ static int kinfo_kstack_compare(const void *a, const void *b) { return ((const struct kinfo_kstack *)a)->kkst_tid - ((const struct kinfo_kstack *)b)->kkst_tid; } static void kinfo_kstack_sort(struct kinfo_kstack *kkstp, int count) { qsort(kkstp, count, sizeof(*kkstp), kinfo_kstack_compare); } void procstat_kstack(struct procstat *procstat, struct kinfo_proc *kipp) { struct kinfo_kstack *kkstp, *kkstp_free; struct kinfo_proc *kip, *kip_free; char trace[KKST_MAXLEN], encoded_trace[KKST_MAXLEN]; unsigned int i, j; unsigned int kip_count, kstk_count; if ((procstat_opts & PS_OPT_NOHEADER) == 0) xo_emit("{T:/%5s %6s %-19s %-19s %-29s}\n", "PID", "TID", "COMM", "TDNAME", "KSTACK"); kkstp = kkstp_free = procstat_getkstack(procstat, kipp, &kstk_count); if (kkstp == NULL) return; /* * We need to re-query for thread information, so don't use *kipp. */ kip = kip_free = procstat_getprocs(procstat, KERN_PROC_PID | KERN_PROC_INC_THREAD, kipp->ki_pid, &kip_count); if (kip == NULL) { procstat_freekstack(procstat, kkstp_free); return; } kinfo_kstack_sort(kkstp, kstk_count); for (i = 0; i < kstk_count; i++) { kkstp = &kkstp_free[i]; /* * Look up the specific thread using its tid so we can * display the per-thread command line. */ kipp = NULL; for (j = 0; j < kip_count; j++) { kipp = &kip_free[j]; if (kkstp->kkst_tid == kipp->ki_tid) break; } if (kipp == NULL) continue; xo_emit("{k:process_id/%5d/%d} ", kipp->ki_pid); xo_emit("{:thread_id/%6d/%d} ", kkstp->kkst_tid); xo_emit("{:command/%-19s/%s} ", kipp->ki_comm); xo_emit("{:thread_name/%-19s/%s} ", kinfo_proc_thread_name(kipp)); switch (kkstp->kkst_state) { case KKST_STATE_RUNNING: xo_emit("{:state/%-29s/%s}\n", ""); continue; case KKST_STATE_SWAPPED: xo_emit("{:state/%-29s/%s}\n", ""); continue; case KKST_STATE_STACKOK: break; default: xo_emit("{:state/%-29s/%s}\n", ""); continue; } /* * The kernel generates a trace with carriage returns between * entries, but for a more compact view, we convert carriage * returns to spaces. */ kstack_cleanup(kkstp->kkst_trace, trace, (procstat_opts & PS_OPT_VERBOSE) != 0 ? 2 : 1); xo_open_list("trace"); kstack_cleanup_encoded(kkstp->kkst_trace, encoded_trace, (procstat_opts & PS_OPT_VERBOSE) != 0 ? 2 : 1); xo_close_list("trace"); xo_emit("{d:trace/%-29s}\n", trace); } procstat_freekstack(procstat, kkstp_free); procstat_freeprocs(procstat, kip_free); } diff --git a/usr.bin/procstat/procstat_penv.c b/usr.bin/procstat/procstat_penv.c index 188f761149e5..b8f3c44a7b04 100644 --- a/usr.bin/procstat/procstat_penv.c +++ b/usr.bin/procstat/procstat_penv.c @@ -1,84 +1,83 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020 Juraj Lutter * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include "procstat.h" void procstat_pargs(struct procstat *procstat, struct kinfo_proc *kipp) { int i; char **args; args = procstat_getargv(procstat, kipp, 0); xo_emit("{k:process_id/%d}: {:command/%s/%s}\n", kipp->ki_pid, kipp->ki_comm); if (args == NULL) { xo_emit("{d:args/-}\n"); } else { for (i = 0; args[i] != NULL; i++) { xo_emit("{Ld:argv[}{Ld:/%d}{Ldwc:]}{l:argv/%s}\n", i, args[i]); } } } void procstat_penv(struct procstat *procstat, struct kinfo_proc *kipp) { int i; char **envs; envs = procstat_getenvv(procstat, kipp, 0); xo_emit("{k:process_id/%d}: {:command/%s/%s}\n", kipp->ki_pid, kipp->ki_comm); if (envs == NULL) { xo_emit("{d:env/-}\n"); } else { for (i = 0; envs[i] != NULL; i++) { xo_emit("{Ld:envp[}{Ld:/%d}{Ldwc:]}{l:envp/%s}\n", i, envs[i]); } } } diff --git a/usr.bin/procstat/procstat_ptlwpinfo.c b/usr.bin/procstat/procstat_ptlwpinfo.c index b2b27a4746ab..bce5119516e3 100644 --- a/usr.bin/procstat/procstat_ptlwpinfo.c +++ b/usr.bin/procstat/procstat_ptlwpinfo.c @@ -1,93 +1,92 @@ /*- * Copyright (c) 2017 Dell EMC * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include "procstat.h" void procstat_ptlwpinfo(struct procstat *prstat, struct kinfo_proc *kipp __unused) { struct ptrace_lwpinfo *pl; unsigned int count, i; pl = procstat_getptlwpinfo(prstat, &count); if (pl == NULL) return; if ((procstat_opts & PS_OPT_NOHEADER) == 0) xo_emit( "{T:/%6s %7s %5s %5s %5s %6s %5s} {[:/%d}{T:/%s}{]:} {T:/%s}\n", "LWPID", "EVENT", "SIGNO", "CODE", "ERRNO", "PID", "UID", 2 * sizeof(void *) + 2, "ADDR", "TDNAME"); xo_open_container("threads"); for (i = 0; i < count; i++) { xo_open_container("thread"); xo_emit("{:lwpid/%6d} ", pl[i].pl_lwpid); switch (pl[i].pl_event) { case PL_EVENT_NONE: xo_emit("{eq:event/none}{d:event/%7s} ", "none"); break; case PL_EVENT_SIGNAL: xo_emit("{eq:event/signal}{d:event/%7s} ", "signal"); break; default: xo_emit("{eq:event/unknown}{d:event/%7s} ", "?"); break; } if ((pl[i].pl_flags & PL_FLAG_SI) != 0) { siginfo_t *si; si = &pl[i].pl_siginfo; xo_emit("{:signal_number/%5d} ", si->si_signo); xo_emit("{:code/%5d} ", si->si_code); xo_emit("{:signal_errno/%5d} ", si->si_errno); xo_emit("{:process_id/%6d} ", si->si_pid); xo_emit("{:user_id/%5d} ", si->si_uid); xo_emit("{[:/%d}{:address/%p}{]:} ", 2 * sizeof(void *) + 2, si->si_addr); } else { xo_emit("{:signal_number/%5s} ", "-"); xo_emit("{:code/%5s} ", "-"); xo_emit("{:signal_errno/%5s} ", "-"); xo_emit("{:process_id/%6s} ", "-"); xo_emit("{:user_id/%5s} ", "-"); xo_emit("{[:/%d}{:address/%s}{]:} ", 2 * sizeof(void *) + 2, "-"); } xo_emit("{:tdname/%s}\n", pl[i].pl_tdname); xo_close_container("thread"); } xo_close_container("threads"); procstat_freeptlwpinfo(prstat, pl); } diff --git a/usr.bin/procstat/procstat_pwdx.c b/usr.bin/procstat/procstat_pwdx.c index f364856b0b27..ee07dd6ed855 100644 --- a/usr.bin/procstat/procstat_pwdx.c +++ b/usr.bin/procstat/procstat_pwdx.c @@ -1,68 +1,67 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020 Juraj Lutter * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "procstat.h" void procstat_pwdx(struct procstat *procstat, struct kinfo_proc *kipp) { struct filestat_list *head; struct filestat *fst; head = procstat_getfiles(procstat, kipp, 0); if (head == NULL) return; STAILQ_FOREACH(fst, head, next) { if ((fst->fs_uflags & PS_FST_UFLAG_CDIR) && (fst->fs_path != NULL)) { xo_emit("{k:process_id/%d}{P:: }", kipp->ki_pid); xo_emit("{:cwd/%s}", fst->fs_path); xo_emit("\n"); } } procstat_freefiles(procstat, head); } diff --git a/usr.bin/procstat/procstat_rlimit.c b/usr.bin/procstat/procstat_rlimit.c index f2dcfcd47327..1881f8ddbebf 100644 --- a/usr.bin/procstat/procstat_rlimit.c +++ b/usr.bin/procstat/procstat_rlimit.c @@ -1,128 +1,127 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2011 Mikolaj Golub * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "procstat.h" static struct { const char *name; const char *suffix; } rlimit_param[15] = { {"cputime", "sec"}, {"filesize", "B "}, {"datasize", "B "}, {"stacksize", "B "}, {"coredumpsize", "B "}, {"memoryuse", "B "}, {"memorylocked", "B "}, {"maxprocesses", " "}, {"openfiles", " "}, {"sbsize", "B "}, {"vmemoryuse", "B "}, {"pseudo-terminals", " "}, {"swapuse", "B "}, {"kqueues", " "}, {"umtxp", " "}, }; #if RLIM_NLIMITS > 15 #error "Resource limits have grown. Add new entries to rlimit_param[]." #endif static const char * humanize_rlimit(int indx, rlim_t limit) { static char buf[14]; int scale; if (limit == RLIM_INFINITY) return ("infinity "); scale = humanize_number(buf, sizeof(buf) - 1, (int64_t)limit, rlimit_param[indx].suffix, HN_AUTOSCALE | HN_GETSCALE, HN_DECIMAL); (void)humanize_number(buf, sizeof(buf) - 1, (int64_t)limit, rlimit_param[indx].suffix, HN_AUTOSCALE, HN_DECIMAL); /* Pad with one space if there is no suffix prefix. */ if (scale == 0) sprintf(buf + strlen(buf), " "); return (buf); } void procstat_rlimit(struct procstat *prstat, struct kinfo_proc *kipp) { struct rlimit rlimit; int i; if ((procstat_opts & PS_OPT_NOHEADER) == 0) { xo_emit("{T:/%5s %-16s %-16s %16s %16s}\n", "PID", "COMM", "RLIMIT", "SOFT ", "HARD "); } xo_emit("{ek:process_id/%5d}{e:command/%-16s/%s}", kipp->ki_pid, kipp->ki_comm); for (i = 0; i < RLIM_NLIMITS; i++) { if (procstat_getrlimit(prstat, kipp, i, &rlimit) == -1) return; xo_emit("{dk:process_id/%5d} {d:command/%-16s} " "{d:rlimit_param/%-16s} ", kipp->ki_pid, kipp->ki_comm, rlimit_param[i].name); xo_open_container(rlimit_param[i].name); if (rlimit.rlim_cur == RLIM_INFINITY) xo_emit("{e:soft_limit/infinity}"); else xo_emit("{e:soft_limit/%U}", rlimit.rlim_cur); if (rlimit.rlim_max == RLIM_INFINITY) xo_emit("{e:hard_limit/infinity}"); else xo_emit("{e:hard_limit/%U}", rlimit.rlim_max); xo_close_container(rlimit_param[i].name); xo_emit("{d:rlim_cur/%16s} ", humanize_rlimit(i, rlimit.rlim_cur)); xo_emit("{d:rlim_max/%16s}\n", humanize_rlimit(i, rlimit.rlim_max)); } } diff --git a/usr.bin/procstat/procstat_rusage.c b/usr.bin/procstat/procstat_rusage.c index 5ee01c3211fd..1960828fd1f5 100644 --- a/usr.bin/procstat/procstat_rusage.c +++ b/usr.bin/procstat/procstat_rusage.c @@ -1,195 +1,194 @@ /*- * Copyright (c) 2012 Hudson River Trading LLC * Written by: John H. Baldwin * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include "procstat.h" static struct { const char *ri_name; bool ri_humanize; int ri_scale; } rusage_info[] = { { "maximum RSS", true, 1 }, { "integral shared memory", true, 1 }, { "integral unshared data", true, 1 }, { "integral unshared stack", true, 1 }, { "page reclaims", false, 0 }, { "page faults", false, 0 }, { "swaps", false, 0 }, { "block reads", false, 0 }, { "block writes", false, 0 }, { "messages sent", false, 0 }, { "messages received", false, 0 }, { "signals received", false, 0 }, { "voluntary context switches", false, 0 }, { "involuntary context switches", false, 0 } }; /* xxx days hh:mm:ss.uuuuuu */ static const char * format_time(struct timeval *tv) { static char buffer[32]; int days, hours, minutes, seconds, used; minutes = tv->tv_sec / 60; seconds = tv->tv_sec % 60; hours = minutes / 60; minutes %= 60; days = hours / 24; hours %= 24; used = 0; if (days == 1) used += snprintf(buffer, sizeof(buffer), "1 day "); else if (days > 0) used += snprintf(buffer, sizeof(buffer), "%u days ", days); snprintf(buffer + used, sizeof(buffer) - used, "%02u:%02u:%02u.%06u", hours, minutes, seconds, (unsigned int)tv->tv_usec); return (buffer); } static const char * format_value(long value, bool humanize, int scale) { static char buffer[14]; if (scale != 0) value <<= scale * 10; if (humanize) humanize_number(buffer, sizeof(buffer), value, "B", scale, HN_DECIMAL); else snprintf(buffer, sizeof(buffer), "%ld ", value); return (buffer); } static void print_prefix(struct kinfo_proc *kipp) { xo_emit("{d:process_id/%5d/%d} ", kipp->ki_pid); if ((procstat_opts & PS_OPT_PERTHREAD) != 0) xo_emit("{d:thread_id/%6d/%d} ", kipp->ki_tid); xo_emit("{d:command/%-16s/%s} ", kipp->ki_comm); } static void print_rusage(struct kinfo_proc *kipp) { long *lp; unsigned int i; char *field, *threadid; print_prefix(kipp); xo_emit("{d:resource/%-14s} {d:usage/%29s}{P: }\n", "user time", format_time(&kipp->ki_rusage.ru_utime)); print_prefix(kipp); xo_emit("{d:resource/%-14s} {d:usage/%29s}{P: }\n", "system time", format_time(&kipp->ki_rusage.ru_stime)); if ((procstat_opts & PS_OPT_PERTHREAD) != 0) { asprintf(&threadid, "%d", kipp->ki_tid); if (threadid == NULL) xo_errc(1, ENOMEM, "Failed to allocate memory in print_rusage()"); xo_open_container(threadid); xo_emit("{e:thread_id/%d}", kipp->ki_tid); } else { xo_emit("{e:process_id/%d}", kipp->ki_pid); xo_emit("{e:command/%s}", kipp->ki_comm); } xo_emit("{e:user time/%s}", format_time(&kipp->ki_rusage.ru_utime)); xo_emit("{e:system time/%s}", format_time(&kipp->ki_rusage.ru_stime)); lp = &kipp->ki_rusage.ru_maxrss; for (i = 0; i < nitems(rusage_info); i++) { print_prefix(kipp); asprintf(&field, "{e:%s/%%D}", rusage_info[i].ri_name); if (field == NULL) xo_errc(1, ENOMEM, "Failed to allocate memory in print_rusage()"); xo_emit(field, *lp); free(field); xo_emit("{d:resource/%-32s} {d:usage/%14s}\n", rusage_info[i].ri_name, format_value(*lp, rusage_info[i].ri_humanize, rusage_info[i].ri_scale)); lp++; } if ((procstat_opts & PS_OPT_PERTHREAD) != 0) { xo_close_container(threadid); free(threadid); } } void procstat_rusage(struct procstat *procstat, struct kinfo_proc *kipp) { struct kinfo_proc *kip; unsigned int count, i; if ((procstat_opts & PS_OPT_NOHEADER) == 0) { xo_emit("{d:ta/%5s} ", "PID"); if ((procstat_opts & PS_OPT_PERTHREAD) != 0) xo_emit("{d:tb/%6s} ", "TID"); xo_emit("{d:tc/%-16s %-32s %14s}\n", "COMM", "RESOURCE", "VALUE "); } if ((procstat_opts & PS_OPT_PERTHREAD) == 0) { print_rusage(kipp); return; } xo_emit("{e:process_id/%d}", kipp->ki_pid); xo_emit("{e:command/%s}", kipp->ki_comm); xo_open_container("threads"); kip = procstat_getprocs(procstat, KERN_PROC_PID | KERN_PROC_INC_THREAD, kipp->ki_pid, &count); if (kip == NULL) return; kinfo_proc_sort(kip, count); for (i = 0; i < count; i++) { print_rusage(&kip[i]); } xo_close_container("threads"); procstat_freeprocs(procstat, kip); } diff --git a/usr.bin/procstat/procstat_sigs.c b/usr.bin/procstat/procstat_sigs.c index 973d622f2036..7bea16739b75 100644 --- a/usr.bin/procstat/procstat_sigs.c +++ b/usr.bin/procstat/procstat_sigs.c @@ -1,245 +1,244 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 Konstantin Belousov * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "procstat.h" static void procstat_print_signame(int sig) { char name[12]; int i; if ((procstat_opts & PS_OPT_SIGNUM) == 0 && sig < sys_nsig) { strlcpy(name, sys_signame[sig], sizeof(name)); for (i = 0; name[i] != 0; i++) name[i] = toupper(name[i]); xo_emit("{d:signal/%-7s/%s} ", name); xo_open_container(name); } else { xo_emit("{d:signal/%-7d/%d} ", sig); snprintf(name, 12, "%d", sig); xo_open_container(name); } } static void procstat_close_signame(int sig) { char name[12]; int i; if ((procstat_opts & PS_OPT_SIGNUM) == 0 && sig < sys_nsig) { strlcpy(name, sys_signame[sig], sizeof(name)); for (i = 0; name[i] != 0; i++) name[i] = toupper(name[i]); xo_close_container(name); } else { snprintf(name, 12, "%d", sig); xo_close_container(name); } } static void procstat_print_sig(const sigset_t *set, int sig, char flag) { xo_emit("{d:sigmember/%c}", sigismember(set, sig) ? flag : '-'); switch (flag) { case 'B': xo_emit("{en:mask/%s}", sigismember(set, sig) ? "true" : "false"); break; case 'C': xo_emit("{en:catch/%s}", sigismember(set, sig) ? "true" : "false"); break; case 'P': xo_emit("{en:list/%s}", sigismember(set, sig) ? "true" : "false"); break; case 'I': xo_emit("{en:ignore/%s}", sigismember(set, sig) ? "true" : "false"); break; default: xo_emit("{en:unknown/%s}", sigismember(set, sig) ? "true" : "false"); break; } } void procstat_sigs(struct procstat *prstat __unused, struct kinfo_proc *kipp) { int j; if ((procstat_opts & PS_OPT_NOHEADER) == 0) xo_emit("{T:/%5s %-16s %-7s %4s}\n", "PID", "COMM", "SIG", "FLAGS"); xo_emit("{ek:process_id/%5d/%d}", kipp->ki_pid); xo_emit("{e:command/%-16s/%s}", kipp->ki_comm); xo_open_container("signals"); for (j = 1; j <= _SIG_MAXSIG; j++) { xo_emit("{dk:process_id/%5d/%d} ", kipp->ki_pid); xo_emit("{d:command/%-16s/%s} ", kipp->ki_comm); procstat_print_signame(j); xo_emit(" "); procstat_print_sig(&kipp->ki_siglist, j, 'P'); procstat_print_sig(&kipp->ki_sigignore, j, 'I'); procstat_print_sig(&kipp->ki_sigcatch, j, 'C'); procstat_close_signame(j); xo_emit("\n"); } xo_close_container("signals"); } void procstat_threads_sigs(struct procstat *procstat, struct kinfo_proc *kipp) { struct kinfo_proc *kip; int j; unsigned int count, i; char *threadid; if ((procstat_opts & PS_OPT_NOHEADER) == 0) xo_emit("{T:/%5s %6s %-16s %-7s %4s}\n", "PID", "TID", "COMM", "SIG", "FLAGS"); kip = procstat_getprocs(procstat, KERN_PROC_PID | KERN_PROC_INC_THREAD, kipp->ki_pid, &count); if (kip == NULL) return; xo_emit("{ek:process_id/%5d/%d}", kipp->ki_pid); xo_emit("{e:command/%-16s/%s}", kipp->ki_comm); xo_open_container("threads"); kinfo_proc_sort(kip, count); for (i = 0; i < count; i++) { kipp = &kip[i]; asprintf(&threadid, "%d", kipp->ki_tid); if (threadid == NULL) xo_errc(1, ENOMEM, "Failed to allocate memory in " "procstat_threads_sigs()"); xo_open_container(threadid); xo_emit("{e:thread_id/%6d/%d}", kipp->ki_tid); xo_open_container("signals"); for (j = 1; j <= _SIG_MAXSIG; j++) { xo_emit("{dk:process_id/%5d/%d} ", kipp->ki_pid); xo_emit("{d:thread_id/%6d/%d} ", kipp->ki_tid); xo_emit("{d:command/%-16s/%s} ", kipp->ki_comm); procstat_print_signame(j); xo_emit(" "); procstat_print_sig(&kipp->ki_siglist, j, 'P'); procstat_print_sig(&kipp->ki_sigmask, j, 'B'); procstat_close_signame(j); xo_emit("\n"); } xo_close_container("signals"); xo_close_container(threadid); free(threadid); } xo_close_container("threads"); procstat_freeprocs(procstat, kip); } void procstat_sigfastblock(struct procstat *procstat, struct kinfo_proc *kipp) { struct kinfo_proc *kip; char *threadid; uintptr_t sigfastblk_addr; int error, name[4]; unsigned int count, i; size_t len; bool has_sigfastblk_addr; if ((procstat_opts & PS_OPT_NOHEADER) == 0) xo_emit("{T:/%5s %6s %-16s %-16s}\n", "PID", "TID", "COMM", "SIGFBLK"); kip = procstat_getprocs(procstat, KERN_PROC_PID | KERN_PROC_INC_THREAD, kipp->ki_pid, &count); if (kip == NULL) return; xo_emit("{ek:process_id/%5d/%d}", kipp->ki_pid); xo_emit("{e:command/%-16s/%s}", kipp->ki_comm); xo_open_container("threads"); kinfo_proc_sort(kip, count); for (i = 0; i < count; i++) { kipp = &kip[i]; len = sizeof(sigfastblk_addr); name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = KERN_PROC_SIGFASTBLK; name[3] = kipp->ki_tid; error = sysctl(name, 4, &sigfastblk_addr, &len, NULL, 0); if (error < 0) { if (errno != ESRCH && errno != ENOTTY) { warn("sysctl: kern.proc.fastsigblk: %d", kipp->ki_tid); } has_sigfastblk_addr = false; } else has_sigfastblk_addr = true; asprintf(&threadid, "%d", kipp->ki_tid); if (threadid == NULL) xo_errc(1, ENOMEM, "Failed to allocate memory in " "procstat_sigfastblock()"); xo_open_container(threadid); xo_emit("{dk:process_id/%5d/%d} ", kipp->ki_pid); xo_emit("{d:thread_id/%6d/%d} ", kipp->ki_tid); xo_emit("{d:command/%-16s/%s} ", kipp->ki_comm); xo_emit("{e:sigfastblock/%#-16jx/%#jx}", has_sigfastblk_addr ? (uintmax_t)sigfastblk_addr : (uintmax_t)-1); xo_emit("{d:sigfastblock/%#-16jx/%#jx}", has_sigfastblk_addr ? (uintmax_t)sigfastblk_addr : (uintmax_t)-1); xo_emit("\n"); xo_close_container(threadid); free(threadid); } xo_close_container("threads"); procstat_freeprocs(procstat, kip); } diff --git a/usr.bin/procstat/procstat_threads.c b/usr.bin/procstat/procstat_threads.c index d0679a830441..cebdc25e215c 100644 --- a/usr.bin/procstat/procstat_threads.c +++ b/usr.bin/procstat/procstat_threads.c @@ -1,134 +1,133 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2007 Robert N. M. Watson * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include "procstat.h" void procstat_threads(struct procstat *procstat, struct kinfo_proc *kipp) { struct kinfo_proc *kip; unsigned int count, i; const char *str; char *threadid; if ((procstat_opts & PS_OPT_NOHEADER) == 0) xo_emit("{T:/%5s %6s %-19s %-19s %2s %4s %-7s %-9s}\n", "PID", "TID", "COMM", "TDNAME", "CPU", "PRI", "STATE", "WCHAN"); xo_emit("{ek:process_id/%d}", kipp->ki_pid); xo_emit("{e:command/%s}", strlen(kipp->ki_comm) ? kipp->ki_comm : "-"); xo_open_container("threads"); kip = procstat_getprocs(procstat, KERN_PROC_PID | KERN_PROC_INC_THREAD, kipp->ki_pid, &count); if (kip == NULL) return; kinfo_proc_sort(kip, count); for (i = 0; i < count; i++) { kipp = &kip[i]; asprintf(&threadid, "%d", kipp->ki_tid); if (threadid == NULL) xo_errc(1, ENOMEM, "Failed to allocate memory in " "procstat_threads()"); xo_open_container(threadid); xo_emit("{dk:process_id/%5d/%d} ", kipp->ki_pid); xo_emit("{:thread_id/%6d/%d} ", kipp->ki_tid); xo_emit("{d:command/%-19s/%s} ", strlen(kipp->ki_comm) ? kipp->ki_comm : "-"); xo_emit("{:thread_name/%-19s/%s} ", kinfo_proc_thread_name(kipp)); if (kipp->ki_oncpu != 255) xo_emit("{:cpu/%3d/%d} ", kipp->ki_oncpu); else if (kipp->ki_lastcpu != 255) xo_emit("{:cpu/%3d/%d} ", kipp->ki_lastcpu); else xo_emit("{:cpu/%3s/%s} ", "-"); xo_emit("{:priority/%4d/%d} ", kipp->ki_pri.pri_level); switch (kipp->ki_stat) { case SRUN: str = "run"; break; case SSTOP: str = "stop"; break; case SSLEEP: str = "sleep"; break; case SLOCK: str = "lock"; break; case SWAIT: str = "wait"; break; case SZOMB: str = "zomb"; break; case SIDL: str = "idle"; break; default: str = "??"; break; } xo_emit("{:run_state/%-7s/%s} ", str); if (kipp->ki_kiflag & KI_LOCKBLOCK) { xo_emit("{:lock_name/*%-8s/%s} ", strlen(kipp->ki_lockname) ? kipp->ki_lockname : "-"); } else { xo_emit("{:wait_channel/%-9s/%s} ", strlen(kipp->ki_wmesg) ? kipp->ki_wmesg : "-"); } xo_close_container(threadid); free(threadid); xo_emit("\n"); } xo_close_container("threads"); procstat_freeprocs(procstat, kip); } diff --git a/usr.bin/procstat/procstat_vm.c b/usr.bin/procstat/procstat_vm.c index 26a8fee57d1c..843c941c89c8 100644 --- a/usr.bin/procstat/procstat_vm.c +++ b/usr.bin/procstat/procstat_vm.c @@ -1,173 +1,172 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2007 Robert N. M. Watson * Copyright (c) 2015 Allan Jude * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include "procstat.h" void procstat_vm(struct procstat *procstat, struct kinfo_proc *kipp) { struct kinfo_vmentry *freep, *kve; int ptrwidth; int i, cnt; const char *str, *lstr; ptrwidth = 2*sizeof(void *) + 2; if ((procstat_opts & PS_OPT_NOHEADER) == 0) xo_emit("{T:/%5s %*s %*s %3s %4s %4s %3s %3s %-5s %-2s %-s}\n", "PID", ptrwidth, "START", ptrwidth, "END", "PRT", "RES", "PRES", "REF", "SHD", "FLAG", "TP", "PATH"); xo_emit("{ek:process_id/%d}", kipp->ki_pid); freep = procstat_getvmmap(procstat, kipp, &cnt); if (freep == NULL) return; xo_open_list("vm"); for (i = 0; i < cnt; i++) { xo_open_instance("vm"); kve = &freep[i]; xo_emit("{dk:process_id/%5d} ", kipp->ki_pid); xo_emit("{d:kve_start/%#*jx} ", ptrwidth, (uintmax_t)kve->kve_start); xo_emit("{d:kve_end/%#*jx} ", ptrwidth, (uintmax_t)kve->kve_end); xo_emit("{e:kve_start/%#jx}", (uintmax_t)kve->kve_start); xo_emit("{e:kve_end/%#jx}", (uintmax_t)kve->kve_end); xo_emit("{d:read/%s}", kve->kve_protection & KVME_PROT_READ ? "r" : "-"); xo_emit("{d:write/%s}", kve->kve_protection & KVME_PROT_WRITE ? "w" : "-"); xo_emit("{d:exec/%s} ", kve->kve_protection & KVME_PROT_EXEC ? "x" : "-"); xo_open_container("kve_protection"); xo_emit("{en:read/%s}", kve->kve_protection & KVME_PROT_READ ? "true" : "false"); xo_emit("{en:write/%s}", kve->kve_protection & KVME_PROT_WRITE ? "true" : "false"); xo_emit("{en:exec/%s}", kve->kve_protection & KVME_PROT_EXEC ? "true" : "false"); xo_close_container("kve_protection"); xo_emit("{:kve_resident/%4d/%d} ", kve->kve_resident); xo_emit("{:kve_private_resident/%4d/%d} ", kve->kve_private_resident); xo_emit("{:kve_ref_count/%3d/%d} ", kve->kve_ref_count); xo_emit("{:kve_shadow_count/%3d/%d} ", kve->kve_shadow_count); xo_emit("{d:copy_on_write/%-1s}", kve->kve_flags & KVME_FLAG_COW ? "C" : "-"); xo_emit("{d:need_copy/%-1s}", kve->kve_flags & KVME_FLAG_NEEDS_COPY ? "N" : "-"); xo_emit("{d:super_pages/%-1s}", kve->kve_flags & KVME_FLAG_SUPER ? "S" : "-"); xo_emit("{d:grows_down/%-1s}", kve->kve_flags & KVME_FLAG_GROWS_UP ? "U" : kve->kve_flags & KVME_FLAG_GROWS_DOWN ? "D" : "-"); xo_emit("{d:wired/%-1s} ", kve->kve_flags & KVME_FLAG_USER_WIRED ? "W" : "-"); xo_open_container("kve_flags"); xo_emit("{en:copy_on_write/%s}", kve->kve_flags & KVME_FLAG_COW ? "true" : "false"); xo_emit("{en:needs_copy/%s}", kve->kve_flags & KVME_FLAG_NEEDS_COPY ? "true" : "false"); xo_emit("{en:super_pages/%s}", kve->kve_flags & KVME_FLAG_SUPER ? "true" : "false"); xo_emit("{en:grows_up/%s}", kve->kve_flags & KVME_FLAG_GROWS_UP ? "true" : "false"); xo_emit("{en:grows_down/%s}", kve->kve_flags & KVME_FLAG_GROWS_DOWN ? "true" : "false"); xo_emit("{en:wired/%s}", kve->kve_flags & KVME_FLAG_USER_WIRED ? "true" : "false"); xo_close_container("kve_flags"); switch (kve->kve_type) { case KVME_TYPE_NONE: str = "--"; lstr = "none"; break; case KVME_TYPE_DEFAULT: str = "df"; lstr = "default"; break; case KVME_TYPE_VNODE: str = "vn"; lstr = "vnode"; break; case KVME_TYPE_SWAP: str = "sw"; lstr = "swap"; break; case KVME_TYPE_DEVICE: str = "dv"; lstr = "device"; break; case KVME_TYPE_PHYS: str = "ph"; lstr = "physical"; break; case KVME_TYPE_DEAD: str = "dd"; lstr = "dead"; break; case KVME_TYPE_SG: str = "sg"; lstr = "scatter/gather"; break; case KVME_TYPE_MGTDEVICE: str = "md"; lstr = "managed_device"; break; case KVME_TYPE_GUARD: str = "gd"; lstr = "guard"; break; case KVME_TYPE_UNKNOWN: default: str = "??"; lstr = "unknown"; break; } xo_emit("{d:kve_type/%-2s} ", str); xo_emit("{e:kve_type/%s}", lstr); xo_emit("{:kve_path/%-s/%s}\n", kve->kve_path); xo_close_instance("vm"); } xo_close_list("vm"); free(freep); } diff --git a/usr.bin/quota/quota.c b/usr.bin/quota/quota.c index dd061d54809b..b5d28fd7c184 100644 --- a/usr.bin/quota/quota.c +++ b/usr.bin/quota/quota.c @@ -1,687 +1,687 @@ /* * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Robert Elz at The University of Melbourne. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Disk quota reporting program. */ -#include + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *qfextension[] = INITQFNAMES; struct quotause { struct quotause *next; long flags; struct dqblk dqblk; char fsname[MAXPATHLEN + 1]; }; static char *timeprt(int64_t seconds); static struct quotause *getprivs(long id, int quotatype); static void usage(void) __dead2; static int showuid(u_long uid); static int showgid(u_long gid); static int showusrname(char *name); static int showgrpname(char *name); static int showquotas(int type, u_long id, const char *name); static void showrawquotas(int type, u_long id, struct quotause *qup); static void heading(int type, u_long id, const char *name, const char *tag); static int getufsquota(struct fstab *fs, struct quotause *qup, long id, int quotatype); static int getnfsquota(struct statfs *fst, struct quotause *qup, long id, int quotatype); static enum clnt_stat callaurpc(char *host, int prognum, int versnum, int procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out); static int alldigits(char *s); static int hflag; static int lflag; static int rflag; static int qflag; static int vflag; static char *filename = NULL; int main(int argc, char *argv[]) { int ngroups; gid_t mygid, gidset[NGROUPS]; int i, ch, gflag = 0, uflag = 0, errflag = 0; while ((ch = getopt(argc, argv, "f:ghlrquv")) != -1) { switch(ch) { case 'f': filename = optarg; break; case 'g': gflag++; break; case 'h': hflag++; break; case 'l': lflag++; break; case 'q': qflag++; break; case 'r': rflag++; break; case 'u': uflag++; break; case 'v': vflag++; break; default: usage(); } } argc -= optind; argv += optind; if (!uflag && !gflag) uflag++; if (argc == 0) { if (uflag) errflag += showuid(getuid()); if (gflag) { mygid = getgid(); ngroups = getgroups(NGROUPS, gidset); if (ngroups < 0) err(1, "getgroups"); errflag += showgid(mygid); for (i = 0; i < ngroups; i++) if (gidset[i] != mygid) errflag += showgid(gidset[i]); } return(errflag); } if (uflag && gflag) usage(); if (uflag) { for (; argc > 0; argc--, argv++) { if (alldigits(*argv)) errflag += showuid(atoi(*argv)); else errflag += showusrname(*argv); } return(errflag); } if (gflag) { for (; argc > 0; argc--, argv++) { if (alldigits(*argv)) errflag += showgid(atoi(*argv)); else errflag += showgrpname(*argv); } } return(errflag); } static void usage(void) { fprintf(stderr, "%s\n%s\n%s\n", "usage: quota [-ghlu] [-f path] [-v | -q | -r]", " quota [-hlu] [-f path] [-v | -q | -r] user ...", " quota -g [-hl] [-f path] [-v | -q | -r] group ..."); exit(1); } /* * Print out quotas for a specified user identifier. */ static int showuid(u_long uid) { struct passwd *pwd = getpwuid(uid); const char *name; if (pwd == NULL) name = "(no account)"; else name = pwd->pw_name; return(showquotas(USRQUOTA, uid, name)); } /* * Print out quotas for a specified user name. */ static int showusrname(char *name) { struct passwd *pwd = getpwnam(name); if (pwd == NULL) { warnx("%s: unknown user", name); return(1); } return(showquotas(USRQUOTA, pwd->pw_uid, name)); } /* * Print out quotas for a specified group identifier. */ static int showgid(u_long gid) { struct group *grp = getgrgid(gid); const char *name; if (grp == NULL) name = "(no entry)"; else name = grp->gr_name; return(showquotas(GRPQUOTA, gid, name)); } /* * Print out quotas for a specified group name. */ static int showgrpname(char *name) { struct group *grp = getgrnam(name); if (grp == NULL) { warnx("%s: unknown group", name); return(1); } return(showquotas(GRPQUOTA, grp->gr_gid, name)); } static void prthumanval(int len, u_int64_t bytes) { char buf[len + 1]; /* * Limit the width to 5 bytes as that is what users expect. */ humanize_number(buf, MIN(sizeof(buf), 5), bytes, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); (void)printf(" %*s", len, buf); } static int showquotas(int type, u_long id, const char *name) { struct quotause *qup; struct quotause *quplist; const char *msgi, *msgb; const char *nam; char *bgrace = NULL, *igrace = NULL; int lines = 0, overquota = 0; static time_t now; if (now == 0) time(&now); quplist = getprivs(id, type); for (qup = quplist; qup; qup = qup->next) { msgi = NULL; if (qup->dqblk.dqb_ihardlimit && qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit) { overquota++; msgi = "File limit reached on"; } else if (qup->dqblk.dqb_isoftlimit && qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) { overquota++; if (qup->dqblk.dqb_itime > now) msgi = "In file grace period on"; else msgi = "Over file quota on"; } msgb = NULL; if (qup->dqblk.dqb_bhardlimit && qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit) { overquota++; msgb = "Block limit reached on"; } else if (qup->dqblk.dqb_bsoftlimit && qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit) { overquota++; if (qup->dqblk.dqb_btime > now) msgb = "In block grace period on"; else msgb = "Over block quota on"; } if (rflag) { showrawquotas(type, id, qup); continue; } if (!vflag && qup->dqblk.dqb_isoftlimit == 0 && qup->dqblk.dqb_ihardlimit == 0 && qup->dqblk.dqb_bsoftlimit == 0 && qup->dqblk.dqb_bhardlimit == 0) continue; if (qflag) { if ((msgi != NULL || msgb != NULL) && lines++ == 0) heading(type, id, name, ""); if (msgi != NULL) printf("\t%s %s\n", msgi, qup->fsname); if (msgb != NULL) printf("\t%s %s\n", msgb, qup->fsname); continue; } if (!vflag && qup->dqblk.dqb_curblocks == 0 && qup->dqblk.dqb_curinodes == 0) continue; if (lines++ == 0) heading(type, id, name, ""); nam = qup->fsname; if (strlen(qup->fsname) > 15) { printf("%s\n", qup->fsname); nam = ""; } printf("%-15s", nam); if (hflag) { prthumanval(7, dbtob(qup->dqblk.dqb_curblocks)); printf("%c", (msgb == NULL) ? ' ' : '*'); prthumanval(7, dbtob(qup->dqblk.dqb_bsoftlimit)); prthumanval(7, dbtob(qup->dqblk.dqb_bhardlimit)); } else { printf(" %7ju%c %7ju %7ju", (uintmax_t)dbtob(qup->dqblk.dqb_curblocks) / 1024, (msgb == NULL) ? ' ' : '*', (uintmax_t)dbtob(qup->dqblk.dqb_bsoftlimit) / 1024, (uintmax_t)dbtob(qup->dqblk.dqb_bhardlimit) / 1024); } if (msgb != NULL) bgrace = timeprt(qup->dqblk.dqb_btime); if (msgi != NULL) igrace = timeprt(qup->dqblk.dqb_itime); printf("%8s %6ju%c %6ju %6ju%8s\n" , (msgb == NULL) ? "" : bgrace , (uintmax_t)qup->dqblk.dqb_curinodes , (msgi == NULL) ? ' ' : '*' , (uintmax_t)qup->dqblk.dqb_isoftlimit , (uintmax_t)qup->dqblk.dqb_ihardlimit , (msgi == NULL) ? "" : igrace ); if (msgb != NULL) free(bgrace); if (msgi != NULL) free(igrace); } if (!qflag && !rflag && lines == 0) heading(type, id, name, "none"); return (overquota); } static void showrawquotas(int type, u_long id, struct quotause *qup) { time_t t; printf("Raw %s quota information for id %lu on %s\n", type == USRQUOTA ? "user" : "group", id, qup->fsname); printf("block hard limit: %ju\n", (uintmax_t)qup->dqblk.dqb_bhardlimit); printf("block soft limit: %ju\n", (uintmax_t)qup->dqblk.dqb_bsoftlimit); printf("current block count: %ju\n", (uintmax_t)qup->dqblk.dqb_curblocks); printf("i-node hard limit: %ju\n", (uintmax_t)qup->dqblk.dqb_ihardlimit); printf("i-node soft limit: %ju\n", (uintmax_t)qup->dqblk.dqb_isoftlimit); printf("current i-node count: %ju\n", (uintmax_t)qup->dqblk.dqb_curinodes); printf("block grace time: %jd", (intmax_t)qup->dqblk.dqb_btime); if (qup->dqblk.dqb_btime != 0) { t = qup->dqblk.dqb_btime; printf(" %s", ctime(&t)); } else { printf("\n"); } printf("i-node grace time: %jd", (intmax_t)qup->dqblk.dqb_itime); if (qup->dqblk.dqb_itime != 0) { t = qup->dqblk.dqb_itime; printf(" %s", ctime(&t)); } else { printf("\n"); } } static void heading(int type, u_long id, const char *name, const char *tag) { printf("Disk quotas for %s %s (%cid %lu): %s\n", qfextension[type], name, *qfextension[type], id, tag); if (!qflag && tag[0] == '\0') { printf("%-15s %7s %8s %7s %7s %6s %7s %6s%8s\n" , "Filesystem" , "usage" , "quota" , "limit" , "grace" , "files" , "quota" , "limit" , "grace" ); } } /* * Calculate the grace period and return a printable string for it. */ static char * timeprt(int64_t seconds) { time_t hours, minutes; char *buf; static time_t now; if (now == 0) time(&now); if (now > seconds) { if ((buf = strdup("none")) == NULL) errx(1, "strdup() failed in timeprt()"); return (buf); } seconds -= now; minutes = (seconds + 30) / 60; hours = (minutes + 30) / 60; if (hours >= 36) { if (asprintf(&buf, "%lddays", ((long)hours + 12) / 24) < 0) errx(1, "asprintf() failed in timeprt(1)"); return (buf); } if (minutes >= 60) { if (asprintf(&buf, "%2ld:%ld", (long)minutes / 60, (long)minutes % 60) < 0) errx(1, "asprintf() failed in timeprt(2)"); return (buf); } if (asprintf(&buf, "%2ld", (long)minutes) < 0) errx(1, "asprintf() failed in timeprt(3)"); return (buf); } /* * Collect the requested quota information. */ static struct quotause * getprivs(long id, int quotatype) { struct quotause *qup, *quptail = NULL; struct fstab *fs; struct quotause *quphead; struct statfs *fst; int nfst, i; struct statfs sfb; qup = quphead = (struct quotause *)0; if (filename != NULL && statfs(filename, &sfb) != 0) err(1, "cannot statfs %s", filename); nfst = getmntinfo(&fst, MNT_NOWAIT); if (nfst == 0) errx(2, "no filesystems mounted!"); setfsent(); for (i = 0; i < nfst; i++) { if (qup == NULL) { if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) errx(2, "out of memory"); } /* * See if the user requested a specific file system * or specified a file inside a mounted file system. */ if (filename != NULL && strcmp(sfb.f_mntonname, fst[i].f_mntonname) != 0) continue; if (strcmp(fst[i].f_fstypename, "nfs") == 0) { if (lflag) continue; if (getnfsquota(&fst[i], qup, id, quotatype) == 0) continue; } else if (strcmp(fst[i].f_fstypename, "ufs") == 0) { /* * XXX * UFS filesystems must be in /etc/fstab, and must * indicate that they have quotas on (?!) This is quite * unlike SunOS where quotas can be enabled/disabled * on a filesystem independent of /etc/fstab, and it * will still print quotas for them. */ if ((fs = getfsspec(fst[i].f_mntfromname)) == NULL) continue; if (getufsquota(fs, qup, id, quotatype) == 0) continue; } else continue; strcpy(qup->fsname, fst[i].f_mntonname); if (quphead == NULL) quphead = qup; else quptail->next = qup; quptail = qup; quptail->next = 0; qup = NULL; } if (qup) free(qup); endfsent(); return (quphead); } /* * Check to see if a particular quota is available. */ static int getufsquota(struct fstab *fs, struct quotause *qup, long id, int quotatype) { struct quotafile *qf; if ((qf = quota_open(fs, quotatype, O_RDONLY)) == NULL) return (0); if (quota_read(qf, &qup->dqblk, id) != 0) return (0); quota_close(qf); return (1); } static int getnfsquota(struct statfs *fst, struct quotause *qup, long id, int quotatype) { struct ext_getquota_args gq_args; struct getquota_args old_gq_args; struct getquota_rslt gq_rslt; struct dqblk *dqp = &qup->dqblk; struct timeval tv; char *cp, host[NI_MAXHOST]; enum clnt_stat call_stat; if (fst->f_flags & MNT_LOCAL) return (0); /* * must be some form of "hostname:/path" */ cp = fst->f_mntfromname; do { cp = strrchr(cp, ':'); } while (cp != NULL && *(cp + 1) != '/'); if (cp == NULL) { warnx("cannot find hostname for %s", fst->f_mntfromname); return (0); } memset(host, 0, sizeof(host)); memcpy(host, fst->f_mntfromname, cp - fst->f_mntfromname); host[sizeof(host) - 1] = '\0'; /* Avoid attempting the RPC for special amd(8) filesystems. */ if (strncmp(fst->f_mntfromname, "pid", 3) == 0 && strchr(fst->f_mntfromname, '@') != NULL) return (0); gq_args.gqa_pathp = cp + 1; gq_args.gqa_id = id; gq_args.gqa_type = quotatype; call_stat = callaurpc(host, RQUOTAPROG, EXT_RQUOTAVERS, RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_ext_getquota_args, (char *)&gq_args, (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt); if (call_stat == RPC_PROGVERSMISMATCH || call_stat == RPC_PROGNOTREGISTERED) { if (quotatype == USRQUOTA) { old_gq_args.gqa_pathp = cp + 1; old_gq_args.gqa_uid = id; call_stat = callaurpc(host, RQUOTAPROG, RQUOTAVERS, RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_getquota_args, (char *)&old_gq_args, (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt); } else { /* Old rpc quota does not support group type */ return (0); } } if (call_stat != 0) return (call_stat); switch (gq_rslt.status) { case Q_NOQUOTA: break; case Q_EPERM: warnx("quota permission error, host: %s", fst->f_mntfromname); break; case Q_OK: gettimeofday(&tv, NULL); /* blocks*/ dqp->dqb_bhardlimit = ((uint64_t)gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit * gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize) / DEV_BSIZE; dqp->dqb_bsoftlimit = ((uint64_t)gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit * gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize) / DEV_BSIZE; dqp->dqb_curblocks = ((uint64_t)gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks * gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize) / DEV_BSIZE; /* inodes */ dqp->dqb_ihardlimit = gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit; dqp->dqb_isoftlimit = gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit; dqp->dqb_curinodes = gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles; /* grace times */ dqp->dqb_btime = tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft; dqp->dqb_itime = tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft; return (1); default: warnx("bad rpc result, host: %s", fst->f_mntfromname); break; } return (0); } static enum clnt_stat callaurpc(char *host, int prognum, int versnum, int procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out) { enum clnt_stat clnt_stat; struct timeval timeout, tottimeout; CLIENT *client = NULL; client = clnt_create(host, prognum, versnum, "udp"); if (client == NULL) return ((int)rpc_createerr.cf_stat); timeout.tv_usec = 0; timeout.tv_sec = 6; CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)(void *)&timeout); client->cl_auth = authunix_create_default(); tottimeout.tv_sec = 25; tottimeout.tv_usec = 0; clnt_stat = clnt_call(client, procnum, inproc, in, outproc, out, tottimeout); return (clnt_stat); } static int alldigits(char *s) { int c; c = *s++; do { if (!isdigit(c)) return (0); } while ((c = *s++)); return (1); } diff --git a/usr.bin/random/randomize_fd.c b/usr.bin/random/randomize_fd.c index 5efd1974b54d..0c505aa1f9f6 100644 --- a/usr.bin/random/randomize_fd.c +++ b/usr.bin/random/randomize_fd.c @@ -1,247 +1,246 @@ /* * Copyright (C) 2003 Sean Chittenden * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include "randomize_fd.h" static struct rand_node *rand_root; static struct rand_node *rand_tail; static struct rand_node * rand_node_allocate(void) { struct rand_node *n; n = (struct rand_node *)malloc(sizeof(struct rand_node)); if (n == NULL) err(1, "malloc"); n->len = 0; n->cp = NULL; n->next = NULL; return(n); } static void rand_node_free(struct rand_node *n) { if (n != NULL) { if (n->cp != NULL) free(n->cp); free(n); } } static void rand_node_free_rec(struct rand_node *n) { if (n != NULL) { if (n->next != NULL) rand_node_free_rec(n->next); rand_node_free(n); } } static void rand_node_append(struct rand_node *n) { if (rand_root == NULL) rand_root = rand_tail = n; else { rand_tail->next = n; rand_tail = n; } } int randomize_fd(int fd, int type, int unique, double denom) { u_char *buf; u_int slen; u_long i, j, numnode, selected; struct rand_node *n, *prev; int bufleft, eof, fndstr, ret; size_t bufc, buflen; ssize_t len; rand_root = rand_tail = NULL; bufc = i = 0; bufleft = eof = fndstr = numnode = 0; if (type == RANDOM_TYPE_UNSET) type = RANDOM_TYPE_LINES; buflen = sizeof(u_char) * MAXBSIZE; buf = (u_char *)malloc(buflen); if (buf == NULL) err(1, "malloc"); while (!eof) { /* Check to see if we have bits in the buffer */ if (bufleft == 0) { len = read(fd, buf, buflen); if (len == -1) err(1, "read"); else if (len == 0) { eof++; break; } else if ((size_t)len < buflen) buflen = (size_t)len; bufleft = (int)len; } /* Look for a newline */ for (i = bufc; i <= buflen && bufleft >= 0; i++, bufleft--) { if (i == buflen) { if (fndstr) { if (!eof) { memmove(buf, &buf[bufc], i - bufc); i -= bufc; bufc = 0; len = read(fd, &buf[i], buflen - i); if (len == -1) err(1, "read"); else if (len == 0) { eof++; break; } else if (len < (ssize_t)(buflen - i)) buflen = i + (size_t)len; bufleft = (int)len; fndstr = 0; } } else { buflen *= 2; buf = (u_char *)realloc(buf, buflen); if (buf == NULL) err(1, "realloc"); if (!eof) { len = read(fd, &buf[i], buflen - i); if (len == -1) err(1, "read"); else if (len == 0) { eof++; break; } else if (len < (ssize_t)(buflen - i)) buflen = i + (size_t)len; bufleft = (int)len; } } } if ((type == RANDOM_TYPE_LINES && buf[i] == '\n') || (type == RANDOM_TYPE_WORDS && isspace(buf[i])) || (eof && i == buflen - 1)) { make_token: if (numnode == UINT32_MAX - 1) { errno = EFBIG; err(1, "too many delimiters"); } numnode++; n = rand_node_allocate(); if (-1 != (int)i) { slen = i - (u_long)bufc; n->len = slen + 2; n->cp = (u_char *)malloc(slen + 2); if (n->cp == NULL) err(1, "malloc"); memmove(n->cp, &buf[bufc], slen); n->cp[slen] = buf[i]; n->cp[slen + 1] = '\0'; bufc = i + 1; } rand_node_append(n); fndstr = 1; } } } /* Necessary evil to compensate for files that don't end with a newline */ if (bufc != i) { i--; goto make_token; } (void)close(fd); free(buf); for (i = numnode; i > 0; i--) { selected = arc4random_uniform(numnode); for (j = 0, prev = n = rand_root; n != NULL; j++, prev = n, n = n->next) { if (j == selected) { if (n->cp == NULL) break; if (random_uniform_denom(denom)) { ret = printf("%.*s", (int)n->len - 1, n->cp); if (ret < 0) err(1, "printf"); } if (unique) { if (n == rand_root) rand_root = n->next; if (n == rand_tail) rand_tail = prev; prev->next = n->next; rand_node_free(n); numnode--; } break; } } } fflush(stdout); if (!unique) rand_node_free_rec(rand_root); return(0); } diff --git a/usr.bin/rctl/rctl.c b/usr.bin/rctl/rctl.c index e7b2ec24bcb4..4227a866dc19 100644 --- a/usr.bin/rctl/rctl.c +++ b/usr.bin/rctl/rctl.c @@ -1,684 +1,683 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RCTL_DEFAULT_BUFSIZE 128 * 1024 static int parse_user(const char *s, id_t *uidp, const char *unexpanded_rule) { char *end; struct passwd *pwd; pwd = getpwnam(s); if (pwd != NULL) { *uidp = pwd->pw_uid; return (0); } if (!isnumber(s[0])) { warnx("malformed rule '%s': unknown user '%s'", unexpanded_rule, s); return (1); } *uidp = strtod(s, &end); if ((size_t)(end - s) != strlen(s)) { warnx("malformed rule '%s': trailing characters " "after numerical id", unexpanded_rule); return (1); } return (0); } static int parse_group(const char *s, id_t *gidp, const char *unexpanded_rule) { char *end; struct group *grp; grp = getgrnam(s); if (grp != NULL) { *gidp = grp->gr_gid; return (0); } if (!isnumber(s[0])) { warnx("malformed rule '%s': unknown group '%s'", unexpanded_rule, s); return (1); } *gidp = strtod(s, &end); if ((size_t)(end - s) != strlen(s)) { warnx("malformed rule '%s': trailing characters " "after numerical id", unexpanded_rule); return (1); } return (0); } /* * Replace human-readable number with its expanded form. */ static char * expand_amount(const char *rule, const char *unexpanded_rule) { uint64_t num; const char *subject, *subject_id, *resource, *action, *amount, *per; char *copy, *expanded, *tofree; int ret; tofree = copy = strdup(rule); if (copy == NULL) { warn("strdup"); return (NULL); } subject = strsep(©, ":"); subject_id = strsep(©, ":"); resource = strsep(©, ":"); action = strsep(©, "=/"); amount = strsep(©, "/"); per = copy; if (amount == NULL || strlen(amount) == 0) { /* * The "copy" has already been tinkered with by strsep(). */ free(tofree); copy = strdup(rule); if (copy == NULL) { warn("strdup"); return (NULL); } return (copy); } assert(subject != NULL); assert(subject_id != NULL); assert(resource != NULL); assert(action != NULL); if (expand_number(amount, &num)) { warnx("malformed rule '%s': invalid numeric value '%s'", unexpanded_rule, amount); free(tofree); return (NULL); } if (per == NULL) { ret = asprintf(&expanded, "%s:%s:%s:%s=%ju", subject, subject_id, resource, action, (uintmax_t)num); } else { ret = asprintf(&expanded, "%s:%s:%s:%s=%ju/%s", subject, subject_id, resource, action, (uintmax_t)num, per); } if (ret <= 0) { warn("asprintf"); free(tofree); return (NULL); } free(tofree); return (expanded); } static char * expand_rule(const char *rule, bool resolve_ids) { id_t id; const char *subject, *textid, *rest; char *copy, *expanded, *resolved, *tofree; int error, ret; tofree = copy = strdup(rule); if (copy == NULL) { warn("strdup"); return (NULL); } subject = strsep(©, ":"); textid = strsep(©, ":"); if (textid == NULL) { warnx("malformed rule '%s': missing subject", rule); return (NULL); } if (copy != NULL) rest = copy; else rest = ""; if (strcasecmp(subject, "u") == 0) subject = "user"; else if (strcasecmp(subject, "g") == 0) subject = "group"; else if (strcasecmp(subject, "p") == 0) subject = "process"; else if (strcasecmp(subject, "l") == 0 || strcasecmp(subject, "c") == 0 || strcasecmp(subject, "class") == 0) subject = "loginclass"; else if (strcasecmp(subject, "j") == 0) subject = "jail"; if (resolve_ids && strcasecmp(subject, "user") == 0 && strlen(textid) > 0) { error = parse_user(textid, &id, rule); if (error != 0) { free(tofree); return (NULL); } ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest); } else if (resolve_ids && strcasecmp(subject, "group") == 0 && strlen(textid) > 0) { error = parse_group(textid, &id, rule); if (error != 0) { free(tofree); return (NULL); } ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest); } else { ret = asprintf(&resolved, "%s:%s:%s", subject, textid, rest); } if (ret <= 0) { warn("asprintf"); free(tofree); return (NULL); } free(tofree); expanded = expand_amount(resolved, rule); free(resolved); return (expanded); } static char * humanize_ids(char *rule) { id_t id; struct passwd *pwd; struct group *grp; const char *subject, *textid, *rest; char *end, *humanized; int ret; subject = strsep(&rule, ":"); textid = strsep(&rule, ":"); if (textid == NULL) errx(1, "rule passed from the kernel didn't contain subject"); if (rule != NULL) rest = rule; else rest = ""; /* Replace numerical user and group ids with names. */ if (strcasecmp(subject, "user") == 0) { id = strtod(textid, &end); if ((size_t)(end - textid) != strlen(textid)) errx(1, "malformed uid '%s'", textid); pwd = getpwuid(id); if (pwd != NULL) textid = pwd->pw_name; } else if (strcasecmp(subject, "group") == 0) { id = strtod(textid, &end); if ((size_t)(end - textid) != strlen(textid)) errx(1, "malformed gid '%s'", textid); grp = getgrgid(id); if (grp != NULL) textid = grp->gr_name; } ret = asprintf(&humanized, "%s:%s:%s", subject, textid, rest); if (ret <= 0) err(1, "asprintf"); return (humanized); } static int str2int64(const char *str, int64_t *value) { char *end; if (str == NULL) return (EINVAL); *value = strtoul(str, &end, 10); if ((size_t)(end - str) != strlen(str)) return (EINVAL); return (0); } static char * humanize_amount(char *rule) { int64_t num; const char *subject, *subject_id, *resource, *action, *amount, *per; char *copy, *humanized, buf[6], *tofree; int ret; tofree = copy = strdup(rule); if (copy == NULL) err(1, "strdup"); subject = strsep(©, ":"); subject_id = strsep(©, ":"); resource = strsep(©, ":"); action = strsep(©, "=/"); amount = strsep(©, "/"); per = copy; if (amount == NULL || strlen(amount) == 0 || str2int64(amount, &num) != 0) { free(tofree); return (rule); } assert(subject != NULL); assert(subject_id != NULL); assert(resource != NULL); assert(action != NULL); if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE) == -1) err(1, "humanize_number"); if (per == NULL) { ret = asprintf(&humanized, "%s:%s:%s:%s=%s", subject, subject_id, resource, action, buf); } else { ret = asprintf(&humanized, "%s:%s:%s:%s=%s/%s", subject, subject_id, resource, action, buf, per); } if (ret <= 0) err(1, "asprintf"); free(tofree); return (humanized); } /* * Print rules, one per line. */ static void print_rules(char *rules, int hflag, int nflag) { char *rule; while ((rule = strsep(&rules, ",")) != NULL) { if (rule[0] == '\0') break; /* XXX */ if (nflag == 0) rule = humanize_ids(rule); if (hflag) rule = humanize_amount(rule); printf("%s\n", rule); } } static void enosys(void) { size_t racct_enable_len; int error; bool racct_enable; racct_enable_len = sizeof(racct_enable); error = sysctlbyname("kern.racct.enable", &racct_enable, &racct_enable_len, NULL, 0); if (error != 0) { if (errno == ENOENT) errx(1, "RACCT/RCTL support not present in kernel; see rctl(8) for details"); err(1, "sysctlbyname"); } if (!racct_enable) errx(1, "RACCT/RCTL present, but disabled; enable using kern.racct.enable=1 tunable"); } static int add_rule(const char *rule, const char *unexpanded_rule) { int error; error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0); if (error != 0) { if (errno == ENOSYS) enosys(); warn("failed to add rule '%s'", unexpanded_rule); } return (error); } static int show_limits(const char *filter, const char *unexpanded_rule, int hflag, int nflag) { int error; char *outbuf = NULL; size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4; for (;;) { outbuflen *= 4; outbuf = realloc(outbuf, outbuflen); if (outbuf == NULL) err(1, "realloc"); error = rctl_get_limits(filter, strlen(filter) + 1, outbuf, outbuflen); if (error == 0) break; if (errno == ERANGE) continue; if (errno == ENOSYS) enosys(); warn("failed to get limits for '%s'", unexpanded_rule); free(outbuf); return (error); } print_rules(outbuf, hflag, nflag); free(outbuf); return (error); } static int remove_rule(const char *filter, const char *unexpanded_rule) { int error; error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0); if (error != 0) { if (errno == ENOSYS) enosys(); warn("failed to remove rule '%s'", unexpanded_rule); } return (error); } static char * humanize_usage_amount(char *usage) { int64_t num; const char *resource, *amount; char *copy, *humanized, buf[6], *tofree; int ret; tofree = copy = strdup(usage); if (copy == NULL) err(1, "strdup"); resource = strsep(©, "="); amount = copy; assert(resource != NULL); assert(amount != NULL); if (str2int64(amount, &num) != 0 || humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE) == -1) { free(tofree); return (usage); } ret = asprintf(&humanized, "%s=%s", resource, buf); if (ret <= 0) err(1, "asprintf"); free(tofree); return (humanized); } /* * Query the kernel about a resource usage and print it out. */ static int show_usage(const char *filter, const char *unexpanded_rule, int hflag) { int error; char *copy, *outbuf = NULL, *tmp; size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4; for (;;) { outbuflen *= 4; outbuf = realloc(outbuf, outbuflen); if (outbuf == NULL) err(1, "realloc"); error = rctl_get_racct(filter, strlen(filter) + 1, outbuf, outbuflen); if (error == 0) break; if (errno == ERANGE) continue; if (errno == ENOSYS) enosys(); warn("failed to show resource consumption for '%s'", unexpanded_rule); free(outbuf); return (error); } copy = outbuf; while ((tmp = strsep(©, ",")) != NULL) { if (tmp[0] == '\0') break; /* XXX */ if (hflag) tmp = humanize_usage_amount(tmp); printf("%s\n", tmp); } free(outbuf); return (error); } /* * Query the kernel about resource limit rules and print them out. */ static int show_rules(const char *filter, const char *unexpanded_rule, int hflag, int nflag) { int error; char *outbuf = NULL; size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4; if (filter != NULL) filterlen = strlen(filter) + 1; else filterlen = 0; for (;;) { outbuflen *= 4; outbuf = realloc(outbuf, outbuflen); if (outbuf == NULL) err(1, "realloc"); error = rctl_get_rules(filter, filterlen, outbuf, outbuflen); if (error == 0) break; if (errno == ERANGE) continue; if (errno == ENOSYS) enosys(); warn("failed to show rules for '%s'", unexpanded_rule); free(outbuf); return (error); } print_rules(outbuf, hflag, nflag); free(outbuf); return (error); } static void usage(void) { fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter " "| -u filter | filter]\n"); exit(1); } int main(int argc, char **argv) { int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0, uflag = 0; char *rule = NULL, *unexpanded_rule; int i, cumulated_error, error; while ((ch = getopt(argc, argv, "ahlnru")) != -1) { switch (ch) { case 'a': aflag = 1; break; case 'h': hflag = 1; break; case 'l': lflag = 1; break; case 'n': nflag = 1; break; case 'r': rflag = 1; break; case 'u': uflag = 1; break; case '?': default: usage(); } } argc -= optind; argv += optind; if (aflag + lflag + rflag + uflag > 1) errx(1, "at most one of -a, -l, -r, or -u may be specified"); if (argc == 0) { if (aflag + lflag + rflag + uflag == 0) { rule = strdup("::"); show_rules(rule, rule, hflag, nflag); return (0); } usage(); } cumulated_error = 0; for (i = 0; i < argc; i++) { unexpanded_rule = argv[i]; /* * Skip resolving if passed -n _and_ -a. Ignore -n otherwise, * so we can still do "rctl -n u:root" and see the rules without * resolving the UID. */ if (aflag != 0 && nflag != 0) rule = expand_rule(unexpanded_rule, false); else rule = expand_rule(unexpanded_rule, true); if (rule == NULL) { cumulated_error++; continue; } /* * The reason for passing the unexpanded_rule is to make * it easier for the user to search for the problematic * rule in the passed input. */ if (aflag) { error = add_rule(rule, unexpanded_rule); } else if (lflag) { error = show_limits(rule, unexpanded_rule, hflag, nflag); } else if (rflag) { error = remove_rule(rule, unexpanded_rule); } else if (uflag) { error = show_usage(rule, unexpanded_rule, hflag); } else { error = show_rules(rule, unexpanded_rule, hflag, nflag); } if (error != 0) cumulated_error++; free(rule); } return (cumulated_error); } diff --git a/usr.bin/rpcinfo/rpcinfo.c b/usr.bin/rpcinfo/rpcinfo.c index 80659a0d5e43..5f2dd4433292 100644 --- a/usr.bin/rpcinfo/rpcinfo.c +++ b/usr.bin/rpcinfo/rpcinfo.c @@ -1,1668 +1,1665 @@ /* $NetBSD: rpcinfo.c,v 1.15 2000/10/04 20:09:05 mjl Exp $ */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ /* * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. */ -#if 0 -#endif - #include /* * rpcinfo: ping a particular rpc program * or dump the registered programs on the remote machine. */ /* * We are for now defining PORTMAP here. It doesn't even compile * unless it is defined. */ #ifndef PORTMAP #define PORTMAP #endif /* * If PORTMAP is defined, rpcinfo will talk to both portmapper and * rpcbind programs; else it talks only to rpcbind. In the latter case * all the portmapper specific options such as -u, -t, -p become void. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PORTMAP /* Support for version 2 portmapper */ #include #include #include #include #include #endif #define MAXHOSTLEN 256 #define MIN_VERS ((u_long) 0) #define MAX_VERS ((u_long) 4294967295UL) #define UNKNOWN "unknown" /* * Functions to be performed. */ #define NONE 0 /* no function */ #define PMAPDUMP 1 /* dump portmapper registrations */ #define TCPPING 2 /* ping TCP service */ #define UDPPING 3 /* ping UDP service */ #define BROADCAST 4 /* ping broadcast service */ #define DELETES 5 /* delete registration for the service */ #define ADDRPING 6 /* pings at the given address */ #define PROGPING 7 /* pings a program on a given host */ #define RPCBDUMP 8 /* dump rpcbind registrations */ #define RPCBDUMP_SHORT 9 /* dump rpcbind registrations - short version */ #define RPCBADDRLIST 10 /* dump addr list about one prog */ #define RPCBGETSTAT 11 /* Get statistics */ struct netidlist { char *netid; struct netidlist *next; }; struct verslist { int vers; struct verslist *next; }; struct rpcbdump_short { u_long prog; struct verslist *vlist; struct netidlist *nlist; struct rpcbdump_short *next; char *owner; }; #ifdef PORTMAP static void ip_ping(u_short, const char *, int, char **); static CLIENT *clnt_com_create(struct sockaddr_in *, u_long, u_long, int *, const char *); static void pmapdump(int, char **); static void get_inet_address(struct sockaddr_in *, char *); #endif static bool_t reply_proc(void *, struct netbuf *, struct netconfig *); static void brdcst(int, char **); static void addrping(char *, char *, int, char **); static void progping(char *, int, char **); static CLIENT *clnt_addr_create(char *, struct netconfig *, u_long, u_long); static CLIENT *clnt_rpcbind_create(char *, int, struct netbuf **); static CLIENT *getclnthandle(char *, struct netconfig *, u_long, struct netbuf **); static CLIENT *local_rpcb(u_long, u_long); static int pstatus(CLIENT *, u_long, u_long); static void rpcbdump(int, char *, int, char **); static void rpcbgetstat(int, char **); static void rpcbaddrlist(char *, int, char **); static void deletereg(char *, int, char **); static void print_rmtcallstat(int, rpcb_stat *); static void print_getaddrstat(int, rpcb_stat *); static void usage(void); static u_long getprognum(char *); static u_long getvers(char *); static char *spaces(int); static bool_t add_version(struct rpcbdump_short *, u_long); static bool_t add_netid(struct rpcbdump_short *, char *); int main(int argc, char **argv) { register int c; int errflg; int function; char *netid = NULL; char *address = NULL; #ifdef PORTMAP char *strptr; u_short portnum = 0; #endif function = NONE; errflg = 0; #ifdef PORTMAP while ((c = getopt(argc, argv, "a:bdlmn:pstT:u")) != -1) { #else while ((c = getopt(argc, argv, "a:bdlmn:sT:")) != -1) { #endif switch (c) { #ifdef PORTMAP case 'p': if (function != NONE) errflg = 1; else function = PMAPDUMP; break; case 't': if (function != NONE) errflg = 1; else function = TCPPING; break; case 'u': if (function != NONE) errflg = 1; else function = UDPPING; break; case 'n': portnum = (u_short) strtol(optarg, &strptr, 10); if (strptr == optarg || *strptr != '\0') errx(1, "%s is illegal port number", optarg); break; #endif case 'a': address = optarg; if (function != NONE) errflg = 1; else function = ADDRPING; break; case 'b': if (function != NONE) errflg = 1; else function = BROADCAST; break; case 'd': if (function != NONE) errflg = 1; else function = DELETES; break; case 'l': if (function != NONE) errflg = 1; else function = RPCBADDRLIST; break; case 'm': if (function != NONE) errflg = 1; else function = RPCBGETSTAT; break; case 's': if (function != NONE) errflg = 1; else function = RPCBDUMP_SHORT; break; case 'T': netid = optarg; break; case '?': errflg = 1; break; } } if (errflg || ((function == ADDRPING) && !netid)) usage(); if (function == NONE) { if (argc - optind > 1) function = PROGPING; else function = RPCBDUMP; } switch (function) { #ifdef PORTMAP case PMAPDUMP: if (portnum != 0) usage(); pmapdump(argc - optind, argv + optind); break; case UDPPING: ip_ping(portnum, "udp", argc - optind, argv + optind); break; case TCPPING: ip_ping(portnum, "tcp", argc - optind, argv + optind); break; #endif case BROADCAST: brdcst(argc - optind, argv + optind); break; case DELETES: deletereg(netid, argc - optind, argv + optind); break; case ADDRPING: addrping(address, netid, argc - optind, argv + optind); break; case PROGPING: progping(netid, argc - optind, argv + optind); break; case RPCBDUMP: case RPCBDUMP_SHORT: rpcbdump(function, netid, argc - optind, argv + optind); break; case RPCBGETSTAT: rpcbgetstat(argc - optind, argv + optind); break; case RPCBADDRLIST: rpcbaddrlist(netid, argc - optind, argv + optind); break; } return (0); } static CLIENT * local_rpcb(u_long prog, u_long vers) { void *localhandle; struct netconfig *nconf; CLIENT *clnt; localhandle = setnetconfig(); while ((nconf = getnetconfig(localhandle)) != NULL) { if (nconf->nc_protofmly != NULL && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) break; } if (nconf == NULL) { warnx("getnetconfig: %s", nc_sperror()); return (NULL); } clnt = clnt_tp_create(NULL, prog, vers, nconf); endnetconfig(localhandle); return clnt; } #ifdef PORTMAP static CLIENT * clnt_com_create(struct sockaddr_in *addr, u_long prog, u_long vers, int *fdp, const char *trans) { CLIENT *clnt; if (strcmp(trans, "tcp") == 0) { clnt = clnttcp_create(addr, prog, vers, fdp, 0, 0); } else { struct timeval to; to.tv_sec = 5; to.tv_usec = 0; clnt = clntudp_create(addr, prog, vers, to, fdp); } if (clnt == (CLIENT *)NULL) { clnt_pcreateerror("rpcinfo"); if (vers == MIN_VERS) printf("program %lu is not available\n", prog); else printf("program %lu version %lu is not available\n", prog, vers); exit(1); } return (clnt); } /* * If portnum is 0, then go and get the address from portmapper, which happens * transparently through clnt*_create(); If version number is not given, it * tries to find out the version number by making a call to version 0 and if * that fails, it obtains the high order and the low order version number. If * version 0 calls succeeds, it tries for MAXVERS call and repeats the same. */ static void ip_ping(u_short portnum, const char *trans, int argc, char **argv) { CLIENT *client; int fd = RPC_ANYFD; struct timeval to; struct sockaddr_in addr; enum clnt_stat rpc_stat; u_long prognum, vers, minvers, maxvers; struct rpc_err rpcerr; int failure = 0; if (argc < 2 || argc > 3) usage(); to.tv_sec = 10; to.tv_usec = 0; prognum = getprognum(argv[1]); get_inet_address(&addr, argv[0]); if (argc == 2) { /* Version number not known */ /* * A call to version 0 should fail with a program/version * mismatch, and give us the range of versions supported. */ vers = MIN_VERS; } else { vers = getvers(argv[2]); } addr.sin_port = htons(portnum); client = clnt_com_create(&addr, prognum, vers, &fd, trans); rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_void, (char *)NULL, to); if (argc != 2) { /* Version number was known */ if (pstatus(client, prognum, vers) < 0) exit(1); (void) CLNT_DESTROY(client); return; } /* Version number not known */ (void) CLNT_CONTROL(client, CLSET_FD_NCLOSE, (char *)NULL); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr(client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * Oh dear, it DOES support version 0. * Let's try version MAX_VERS. */ (void) CLNT_DESTROY(client); addr.sin_port = htons(portnum); client = clnt_com_create(&addr, prognum, MAX_VERS, &fd, trans); rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_void, (char *)NULL, to); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr(client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * It also supports version MAX_VERS. * Looks like we have a wise guy. * OK, we give them information on all * 4 billion versions they support... */ minvers = 0; maxvers = MAX_VERS; } else { (void) pstatus(client, prognum, MAX_VERS); exit(1); } } else { (void) pstatus(client, prognum, (u_long)0); exit(1); } (void) CLNT_DESTROY(client); for (vers = minvers; vers <= maxvers; vers++) { addr.sin_port = htons(portnum); client = clnt_com_create(&addr, prognum, vers, &fd, trans); rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_void, (char *)NULL, to); if (pstatus(client, prognum, vers) < 0) failure = 1; (void) CLNT_DESTROY(client); } if (failure) exit(1); (void) close(fd); return; } /* * Dump all the portmapper registerations */ static void pmapdump(int argc, char **argv) { struct sockaddr_in server_addr; struct pmaplist *head = NULL; int socket = RPC_ANYSOCK; struct timeval minutetimeout; register CLIENT *client; struct rpcent *rpc; enum clnt_stat clnt_st; struct rpc_err err; char *host = NULL; if (argc > 1) usage(); if (argc == 1) { host = argv[0]; get_inet_address(&server_addr, host); server_addr.sin_port = htons(PMAPPORT); client = clnttcp_create(&server_addr, PMAPPROG, PMAPVERS, &socket, 50, 500); } else client = local_rpcb(PMAPPROG, PMAPVERS); if (client == NULL) { if (rpc_createerr.cf_stat == RPC_TLIERROR) { /* * "Misc. TLI error" is not too helpful. Most likely * the connection to the remote server timed out, so * this error is at least less perplexing. */ rpc_createerr.cf_stat = RPC_PMAPFAILURE; rpc_createerr.cf_error.re_status = RPC_FAILED; } clnt_pcreateerror("rpcinfo: can't contact portmapper"); exit(1); } minutetimeout.tv_sec = 60; minutetimeout.tv_usec = 0; clnt_st = CLNT_CALL(client, PMAPPROC_DUMP, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_pmaplist_ptr, (char *)&head, minutetimeout); if (clnt_st != RPC_SUCCESS) { if ((clnt_st == RPC_PROGVERSMISMATCH) || (clnt_st == RPC_PROGUNAVAIL)) { CLNT_GETERR(client, &err); if (err.re_vers.low > PMAPVERS) { if (host) warnx("%s does not support portmapper." "Try rpcinfo %s instead", host, host); else warnx("local host does not support " "portmapper. Try 'rpcinfo' " "instead"); } exit(1); } clnt_perror(client, "rpcinfo: can't contact portmapper"); exit(1); } if (head == NULL) { printf("No remote programs registered.\n"); } else { printf(" program vers proto port service\n"); for (; head != NULL; head = head->pml_next) { printf("%10ld%5ld", head->pml_map.pm_prog, head->pml_map.pm_vers); if (head->pml_map.pm_prot == IPPROTO_UDP) printf("%6s", "udp"); else if (head->pml_map.pm_prot == IPPROTO_TCP) printf("%6s", "tcp"); else if (head->pml_map.pm_prot == IPPROTO_ST) printf("%6s", "local"); else printf("%6ld", head->pml_map.pm_prot); printf("%7ld", head->pml_map.pm_port); rpc = getrpcbynumber(head->pml_map.pm_prog); if (rpc) printf(" %s\n", rpc->r_name); else printf("\n"); } } } static void get_inet_address(struct sockaddr_in *addr, char *host) { struct netconfig *nconf; struct addrinfo hints, *res; int error; (void) memset((char *)addr, 0, sizeof (*addr)); addr->sin_addr.s_addr = inet_addr(host); if (addr->sin_addr.s_addr == INADDR_NONE || addr->sin_addr.s_addr == INADDR_ANY) { if ((nconf = __rpc_getconfip("udp")) == NULL && (nconf = __rpc_getconfip("tcp")) == NULL) errx(1, "couldn't find a suitable transport"); else { memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; if ((error = getaddrinfo(host, "rpcbind", &hints, &res)) != 0) errx(1, "%s: %s", host, gai_strerror(error)); else { memcpy(addr, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); } (void) freenetconfigent(nconf); } } else { addr->sin_family = AF_INET; } } #endif /* PORTMAP */ /* * reply_proc collects replies from the broadcast. * to get a unique list of responses the output of rpcinfo should * be piped through sort(1) and then uniq(1). */ /*ARGSUSED*/ static bool_t reply_proc(void *res, struct netbuf *who, struct netconfig *nconf) /* void *res; Nothing comes back */ /* struct netbuf *who; Who sent us the reply */ /* struct netconfig *nconf; On which transport the reply came */ { char *uaddr; char hostbuf[NI_MAXHOST]; const char *hostname; struct sockaddr *sa = (struct sockaddr *)who->buf; if (getnameinfo(sa, sa->sa_len, hostbuf, NI_MAXHOST, NULL, 0, 0)) { hostname = UNKNOWN; } else { hostname = hostbuf; } uaddr = taddr2uaddr(nconf, who); if (uaddr == NULL) { printf("%s\t%s\n", UNKNOWN, hostname); } else { printf("%s\t%s\n", uaddr, hostname); free((char *)uaddr); } return (FALSE); } static void brdcst(int argc, char **argv) { enum clnt_stat rpc_stat; u_long prognum, vers; if (argc != 2) usage(); prognum = getprognum(argv[0]); vers = getvers(argv[1]); rpc_stat = rpc_broadcast(prognum, vers, NULLPROC, (xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_void, (char *)NULL, (resultproc_t) reply_proc, NULL); if ((rpc_stat != RPC_SUCCESS) && (rpc_stat != RPC_TIMEDOUT)) errx(1, "broadcast failed: %s", clnt_sperrno(rpc_stat)); exit(0); } static bool_t add_version(struct rpcbdump_short *rs, u_long vers) { struct verslist *vl; for (vl = rs->vlist; vl; vl = vl->next) if (vl->vers == vers) break; if (vl) return (TRUE); vl = (struct verslist *)malloc(sizeof (struct verslist)); if (vl == NULL) return (FALSE); vl->vers = vers; vl->next = rs->vlist; rs->vlist = vl; return (TRUE); } static bool_t add_netid(struct rpcbdump_short *rs, char *netid) { struct netidlist *nl; for (nl = rs->nlist; nl; nl = nl->next) if (strcmp(nl->netid, netid) == 0) break; if (nl) return (TRUE); nl = (struct netidlist *)malloc(sizeof (struct netidlist)); if (nl == NULL) return (FALSE); nl->netid = netid; nl->next = rs->nlist; rs->nlist = nl; return (TRUE); } static void rpcbdump(int dumptype, char *netid, int argc, char **argv) { rpcblist_ptr head = NULL; struct timeval minutetimeout; register CLIENT *client; struct rpcent *rpc; char *host; struct netidlist *nl; struct verslist *vl; struct rpcbdump_short *rs, *rs_tail; char buf[256]; enum clnt_stat clnt_st; struct rpc_err err; struct rpcbdump_short *rs_head = NULL; if (argc > 1) usage(); if (argc == 1) { host = argv[0]; if (netid == NULL) { client = clnt_rpcbind_create(host, RPCBVERS, NULL); } else { struct netconfig *nconf; nconf = getnetconfigent(netid); if (nconf == NULL) { nc_perror("rpcinfo: invalid transport"); exit(1); } client = getclnthandle(host, nconf, RPCBVERS, NULL); if (nconf) (void) freenetconfigent(nconf); } } else client = local_rpcb(PMAPPROG, RPCBVERS); if (client == (CLIENT *)NULL) { clnt_pcreateerror("rpcinfo: can't contact rpcbind"); exit(1); } minutetimeout.tv_sec = 60; minutetimeout.tv_usec = 0; clnt_st = CLNT_CALL(client, RPCBPROC_DUMP, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr, (char *) &head, minutetimeout); if (clnt_st != RPC_SUCCESS) { if ((clnt_st == RPC_PROGVERSMISMATCH) || (clnt_st == RPC_PROGUNAVAIL)) { int vers; CLNT_GETERR(client, &err); if (err.re_vers.low == RPCBVERS4) { vers = RPCBVERS4; clnt_control(client, CLSET_VERS, (char *)&vers); clnt_st = CLNT_CALL(client, RPCBPROC_DUMP, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr, (char *) &head, minutetimeout); if (clnt_st != RPC_SUCCESS) goto failed; } else { if (err.re_vers.high == PMAPVERS) { int high, low; struct pmaplist *pmaphead = NULL; rpcblist_ptr list, prev; vers = PMAPVERS; clnt_control(client, CLSET_VERS, (char *)&vers); clnt_st = CLNT_CALL(client, PMAPPROC_DUMP, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_pmaplist_ptr, (char *)&pmaphead, minutetimeout); if (clnt_st != RPC_SUCCESS) goto failed; /* * convert to rpcblist_ptr format */ for (head = NULL; pmaphead != NULL; pmaphead = pmaphead->pml_next) { list = (rpcblist *)malloc(sizeof (rpcblist)); if (list == NULL) goto error; if (head == NULL) head = list; else prev->rpcb_next = (rpcblist_ptr) list; list->rpcb_next = NULL; list->rpcb_map.r_prog = pmaphead->pml_map.pm_prog; list->rpcb_map.r_vers = pmaphead->pml_map.pm_vers; if (pmaphead->pml_map.pm_prot == IPPROTO_UDP) list->rpcb_map.r_netid = "udp"; else if (pmaphead->pml_map.pm_prot == IPPROTO_TCP) list->rpcb_map.r_netid = "tcp"; else { #define MAXLONG_AS_STRING "2147483648" list->rpcb_map.r_netid = malloc(strlen(MAXLONG_AS_STRING) + 1); if (list->rpcb_map.r_netid == NULL) goto error; sprintf(list->rpcb_map.r_netid, "%6ld", pmaphead->pml_map.pm_prot); } list->rpcb_map.r_owner = UNKNOWN; low = pmaphead->pml_map.pm_port & 0xff; high = (pmaphead->pml_map.pm_port >> 8) & 0xff; list->rpcb_map.r_addr = strdup("0.0.0.0.XXX.XXX"); sprintf(&list->rpcb_map.r_addr[8], "%d.%d", high, low); prev = list; } } } } else { /* any other error */ failed: clnt_perror(client, "rpcinfo: can't contact rpcbind: "); exit(1); } } if (head == NULL) { printf("No remote programs registered.\n"); } else if (dumptype == RPCBDUMP) { printf( " program version netid address service owner\n"); for (; head != NULL; head = head->rpcb_next) { printf("%10u%5u ", head->rpcb_map.r_prog, head->rpcb_map.r_vers); printf("%-9s ", head->rpcb_map.r_netid); printf("%-22s", head->rpcb_map.r_addr); rpc = getrpcbynumber(head->rpcb_map.r_prog); if (rpc) printf(" %-10s", rpc->r_name); else printf(" %-10s", "-"); printf(" %s\n", head->rpcb_map.r_owner); } } else if (dumptype == RPCBDUMP_SHORT) { for (; head != NULL; head = head->rpcb_next) { for (rs = rs_head; rs; rs = rs->next) if (head->rpcb_map.r_prog == rs->prog) break; if (rs == NULL) { rs = (struct rpcbdump_short *) malloc(sizeof (struct rpcbdump_short)); if (rs == NULL) goto error; rs->next = NULL; if (rs_head == NULL) { rs_head = rs; rs_tail = rs; } else { rs_tail->next = rs; rs_tail = rs; } rs->prog = head->rpcb_map.r_prog; rs->owner = head->rpcb_map.r_owner; rs->nlist = NULL; rs->vlist = NULL; } if (add_version(rs, head->rpcb_map.r_vers) == FALSE) goto error; if (add_netid(rs, head->rpcb_map.r_netid) == FALSE) goto error; } printf( " program version(s) netid(s) service owner\n"); for (rs = rs_head; rs; rs = rs->next) { char *p = buf; printf("%10ld ", rs->prog); for (vl = rs->vlist; vl; vl = vl->next) { sprintf(p, "%d", vl->vers); p = p + strlen(p); if (vl->next) sprintf(p++, ","); } printf("%-10s", buf); buf[0] = '\0'; for (nl = rs->nlist; nl; nl = nl->next) { strlcat(buf, nl->netid, sizeof(buf)); if (nl->next) strlcat(buf, ",", sizeof(buf)); } printf("%-32s", buf); rpc = getrpcbynumber(rs->prog); if (rpc) printf(" %-11s", rpc->r_name); else printf(" %-11s", "-"); printf(" %s\n", rs->owner); } } clnt_destroy(client); return; error: warnx("no memory"); return; } static char nullstring[] = "\000"; static void rpcbaddrlist(char *netid, int argc, char **argv) { rpcb_entry_list_ptr head = NULL; struct timeval minutetimeout; register CLIENT *client; struct rpcent *rpc; char *host; RPCB parms; struct netbuf *targaddr; if (argc != 3) usage(); host = argv[0]; if (netid == NULL) { client = clnt_rpcbind_create(host, RPCBVERS4, &targaddr); } else { struct netconfig *nconf; nconf = getnetconfigent(netid); if (nconf == NULL) { nc_perror("rpcinfo: invalid transport"); exit(1); } client = getclnthandle(host, nconf, RPCBVERS4, &targaddr); if (nconf) (void) freenetconfigent(nconf); } if (client == (CLIENT *)NULL) { clnt_pcreateerror("rpcinfo: can't contact rpcbind"); exit(1); } minutetimeout.tv_sec = 60; minutetimeout.tv_usec = 0; parms.r_prog = getprognum(argv[1]); parms.r_vers = getvers(argv[2]); parms.r_netid = client->cl_netid; if (targaddr == NULL) { parms.r_addr = nullstring; /* for XDRing */ } else { /* * We also send the remote system the address we * used to contact it in case it can help it * connect back with us */ struct netconfig *nconf; nconf = getnetconfigent(client->cl_netid); if (nconf != NULL) { parms.r_addr = taddr2uaddr(nconf, targaddr); if (parms.r_addr == NULL) parms.r_addr = nullstring; freenetconfigent(nconf); } else { parms.r_addr = nullstring; /* for XDRing */ } free(targaddr->buf); free(targaddr); } parms.r_owner = nullstring; if (CLNT_CALL(client, RPCBPROC_GETADDRLIST, (xdrproc_t) xdr_rpcb, (char *) &parms, (xdrproc_t) xdr_rpcb_entry_list_ptr, (char *) &head, minutetimeout) != RPC_SUCCESS) { clnt_perror(client, "rpcinfo: can't contact rpcbind: "); exit(1); } if (head == NULL) { printf("No remote programs registered.\n"); } else { printf( " program vers tp_family/name/class address\t\t service\n"); for (; head != NULL; head = head->rpcb_entry_next) { rpcb_entry *re; char buf[128]; re = &head->rpcb_entry_map; printf("%10u%3u ", parms.r_prog, parms.r_vers); sprintf(buf, "%s/%s/%s ", re->r_nc_protofmly, re->r_nc_proto, re->r_nc_semantics == NC_TPI_CLTS ? "clts" : re->r_nc_semantics == NC_TPI_COTS ? "cots" : "cots_ord"); printf("%-24s", buf); printf("%-24s", re->r_maddr); rpc = getrpcbynumber(parms.r_prog); if (rpc) printf(" %-13s", rpc->r_name); else printf(" %-13s", "-"); printf("\n"); } } clnt_destroy(client); return; } /* * monitor rpcbind */ static void rpcbgetstat(int argc, char **argv) { rpcb_stat_byvers inf; struct timeval minutetimeout; register CLIENT *client; char *host; int i, j; rpcbs_addrlist *pa; rpcbs_rmtcalllist *pr; int cnt, flen; #define MAXFIELD 64 char fieldbuf[MAXFIELD]; #define MAXLINE 256 char linebuf[MAXLINE]; char *cp, *lp; const char *pmaphdr[] = { "NULL", "SET", "UNSET", "GETPORT", "DUMP", "CALLIT" }; const char *rpcb3hdr[] = { "NULL", "SET", "UNSET", "GETADDR", "DUMP", "CALLIT", "TIME", "U2T", "T2U" }; const char *rpcb4hdr[] = { "NULL", "SET", "UNSET", "GETADDR", "DUMP", "CALLIT", "TIME", "U2T", "T2U", "VERADDR", "INDRECT", "GETLIST", "GETSTAT" }; #define TABSTOP 8 if (argc >= 1) { host = argv[0]; client = clnt_rpcbind_create(host, RPCBVERS4, NULL); } else client = local_rpcb(PMAPPROG, RPCBVERS4); if (client == (CLIENT *)NULL) { clnt_pcreateerror("rpcinfo: can't contact rpcbind"); exit(1); } minutetimeout.tv_sec = 60; minutetimeout.tv_usec = 0; memset((char *)&inf, 0, sizeof (rpcb_stat_byvers)); if (CLNT_CALL(client, RPCBPROC_GETSTAT, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcb_stat_byvers, (char *)&inf, minutetimeout) != RPC_SUCCESS) { clnt_perror(client, "rpcinfo: can't contact rpcbind: "); exit(1); } printf("PORTMAP (version 2) statistics\n"); lp = linebuf; for (i = 0; i <= rpcb_highproc_2; i++) { fieldbuf[0] = '\0'; switch (i) { case PMAPPROC_SET: sprintf(fieldbuf, "%d/", inf[RPCBVERS_2_STAT].setinfo); break; case PMAPPROC_UNSET: sprintf(fieldbuf, "%d/", inf[RPCBVERS_2_STAT].unsetinfo); break; case PMAPPROC_GETPORT: cnt = 0; for (pa = inf[RPCBVERS_2_STAT].addrinfo; pa; pa = pa->next) cnt += pa->success; sprintf(fieldbuf, "%d/", cnt); break; case PMAPPROC_CALLIT: cnt = 0; for (pr = inf[RPCBVERS_2_STAT].rmtinfo; pr; pr = pr->next) cnt += pr->success; sprintf(fieldbuf, "%d/", cnt); break; default: break; /* For the remaining ones */ } cp = &fieldbuf[0] + strlen(fieldbuf); sprintf(cp, "%d", inf[RPCBVERS_2_STAT].info[i]); flen = strlen(fieldbuf); printf("%s%s", pmaphdr[i], spaces((TABSTOP * (1 + flen / TABSTOP)) - strlen(pmaphdr[i]))); sprintf(lp, "%s%s", fieldbuf, spaces(cnt = ((TABSTOP * (1 + flen / TABSTOP)) - flen))); lp += (flen + cnt); } printf("\n%s\n\n", linebuf); if (inf[RPCBVERS_2_STAT].info[PMAPPROC_CALLIT]) { printf("PMAP_RMTCALL call statistics\n"); print_rmtcallstat(RPCBVERS_2_STAT, &inf[RPCBVERS_2_STAT]); printf("\n"); } if (inf[RPCBVERS_2_STAT].info[PMAPPROC_GETPORT]) { printf("PMAP_GETPORT call statistics\n"); print_getaddrstat(RPCBVERS_2_STAT, &inf[RPCBVERS_2_STAT]); printf("\n"); } printf("RPCBIND (version 3) statistics\n"); lp = linebuf; for (i = 0; i <= rpcb_highproc_3; i++) { fieldbuf[0] = '\0'; switch (i) { case RPCBPROC_SET: sprintf(fieldbuf, "%d/", inf[RPCBVERS_3_STAT].setinfo); break; case RPCBPROC_UNSET: sprintf(fieldbuf, "%d/", inf[RPCBVERS_3_STAT].unsetinfo); break; case RPCBPROC_GETADDR: cnt = 0; for (pa = inf[RPCBVERS_3_STAT].addrinfo; pa; pa = pa->next) cnt += pa->success; sprintf(fieldbuf, "%d/", cnt); break; case RPCBPROC_CALLIT: cnt = 0; for (pr = inf[RPCBVERS_3_STAT].rmtinfo; pr; pr = pr->next) cnt += pr->success; sprintf(fieldbuf, "%d/", cnt); break; default: break; /* For the remaining ones */ } cp = &fieldbuf[0] + strlen(fieldbuf); sprintf(cp, "%d", inf[RPCBVERS_3_STAT].info[i]); flen = strlen(fieldbuf); printf("%s%s", rpcb3hdr[i], spaces((TABSTOP * (1 + flen / TABSTOP)) - strlen(rpcb3hdr[i]))); sprintf(lp, "%s%s", fieldbuf, spaces(cnt = ((TABSTOP * (1 + flen / TABSTOP)) - flen))); lp += (flen + cnt); } printf("\n%s\n\n", linebuf); if (inf[RPCBVERS_3_STAT].info[RPCBPROC_CALLIT]) { printf("RPCB_RMTCALL (version 3) call statistics\n"); print_rmtcallstat(RPCBVERS_3_STAT, &inf[RPCBVERS_3_STAT]); printf("\n"); } if (inf[RPCBVERS_3_STAT].info[RPCBPROC_GETADDR]) { printf("RPCB_GETADDR (version 3) call statistics\n"); print_getaddrstat(RPCBVERS_3_STAT, &inf[RPCBVERS_3_STAT]); printf("\n"); } printf("RPCBIND (version 4) statistics\n"); for (j = 0; j <= 9; j += 9) { /* Just two iterations for printing */ lp = linebuf; for (i = j; i <= MAX(8, rpcb_highproc_4 - 9 + j); i++) { fieldbuf[0] = '\0'; switch (i) { case RPCBPROC_SET: sprintf(fieldbuf, "%d/", inf[RPCBVERS_4_STAT].setinfo); break; case RPCBPROC_UNSET: sprintf(fieldbuf, "%d/", inf[RPCBVERS_4_STAT].unsetinfo); break; case RPCBPROC_GETADDR: cnt = 0; for (pa = inf[RPCBVERS_4_STAT].addrinfo; pa; pa = pa->next) cnt += pa->success; sprintf(fieldbuf, "%d/", cnt); break; case RPCBPROC_CALLIT: cnt = 0; for (pr = inf[RPCBVERS_4_STAT].rmtinfo; pr; pr = pr->next) cnt += pr->success; sprintf(fieldbuf, "%d/", cnt); break; default: break; /* For the remaining ones */ } cp = &fieldbuf[0] + strlen(fieldbuf); /* * XXX: We also add RPCBPROC_GETADDRLIST queries to * RPCB_GETADDR because rpcbind includes the * RPCB_GETADDRLIST successes in RPCB_GETADDR. */ if (i != RPCBPROC_GETADDR) sprintf(cp, "%d", inf[RPCBVERS_4_STAT].info[i]); else sprintf(cp, "%d", inf[RPCBVERS_4_STAT].info[i] + inf[RPCBVERS_4_STAT].info[RPCBPROC_GETADDRLIST]); flen = strlen(fieldbuf); printf("%s%s", rpcb4hdr[i], spaces((TABSTOP * (1 + flen / TABSTOP)) - strlen(rpcb4hdr[i]))); sprintf(lp, "%s%s", fieldbuf, spaces(cnt = ((TABSTOP * (1 + flen / TABSTOP)) - flen))); lp += (flen + cnt); } printf("\n%s\n", linebuf); } if (inf[RPCBVERS_4_STAT].info[RPCBPROC_CALLIT] || inf[RPCBVERS_4_STAT].info[RPCBPROC_INDIRECT]) { printf("\n"); printf("RPCB_RMTCALL (version 4) call statistics\n"); print_rmtcallstat(RPCBVERS_4_STAT, &inf[RPCBVERS_4_STAT]); } if (inf[RPCBVERS_4_STAT].info[RPCBPROC_GETADDR]) { printf("\n"); printf("RPCB_GETADDR (version 4) call statistics\n"); print_getaddrstat(RPCBVERS_4_STAT, &inf[RPCBVERS_4_STAT]); } clnt_destroy(client); } /* * Delete registeration for this (prog, vers, netid) */ static void deletereg(char *netid, int argc, char **argv) { struct netconfig *nconf = NULL; if (argc != 2) usage(); if (netid) { nconf = getnetconfigent(netid); if (nconf == NULL) errx(1, "netid %s not supported", netid); } if ((rpcb_unset(getprognum(argv[0]), getvers(argv[1]), nconf)) == 0) errx(1, "could not delete registration for prog %s version %s", argv[0], argv[1]); } /* * Create and return a handle for the given nconf. * Exit if cannot create handle. */ static CLIENT * clnt_addr_create(char *address, struct netconfig *nconf, u_long prog, u_long vers) { CLIENT *client; static struct netbuf *nbuf; static int fd = RPC_ANYFD; if (fd == RPC_ANYFD) { if ((fd = __rpc_nconf2fd(nconf)) == -1) { rpc_createerr.cf_stat = RPC_TLIERROR; clnt_pcreateerror("rpcinfo"); exit(1); } /* Convert the uaddr to taddr */ nbuf = uaddr2taddr(nconf, address); if (nbuf == NULL) errx(1, "no address for client handle"); } client = clnt_tli_create(fd, nconf, nbuf, prog, vers, 0, 0); if (client == (CLIENT *)NULL) { clnt_pcreateerror("rpcinfo"); exit(1); } return (client); } /* * If the version number is given, ping that (prog, vers); else try to find * the version numbers supported for that prog and ping all the versions. * Remote rpcbind is not contacted for this service. The requests are * sent directly to the services themselves. */ static void addrping(char *address, char *netid, int argc, char **argv) { CLIENT *client; struct timeval to; enum clnt_stat rpc_stat; u_long prognum, versnum, minvers, maxvers; struct rpc_err rpcerr; int failure = 0; struct netconfig *nconf; int fd; if (argc < 1 || argc > 2 || (netid == NULL)) usage(); nconf = getnetconfigent(netid); if (nconf == (struct netconfig *)NULL) errx(1, "could not find %s", netid); to.tv_sec = 10; to.tv_usec = 0; prognum = getprognum(argv[0]); if (argc == 1) { /* Version number not known */ /* * A call to version 0 should fail with a program/version * mismatch, and give us the range of versions supported. */ versnum = MIN_VERS; } else { versnum = getvers(argv[1]); } client = clnt_addr_create(address, nconf, prognum, versnum); rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_void, (char *)NULL, to); if (argc == 2) { /* Version number was known */ if (pstatus(client, prognum, versnum) < 0) failure = 1; (void) CLNT_DESTROY(client); if (failure) exit(1); return; } /* Version number not known */ (void) CLNT_CONTROL(client, CLSET_FD_NCLOSE, (char *)NULL); (void) CLNT_CONTROL(client, CLGET_FD, (char *)&fd); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr(client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * Oh dear, it DOES support version 0. * Let's try version MAX_VERS. */ (void) CLNT_DESTROY(client); client = clnt_addr_create(address, nconf, prognum, MAX_VERS); rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_void, (char *)NULL, to); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr(client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * It also supports version MAX_VERS. * Looks like we have a wise guy. * OK, we give them information on all * 4 billion versions they support... */ minvers = 0; maxvers = MAX_VERS; } else { (void) pstatus(client, prognum, MAX_VERS); exit(1); } } else { (void) pstatus(client, prognum, (u_long)0); exit(1); } (void) CLNT_DESTROY(client); for (versnum = minvers; versnum <= maxvers; versnum++) { client = clnt_addr_create(address, nconf, prognum, versnum); rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_void, (char *)NULL, to); if (pstatus(client, prognum, versnum) < 0) failure = 1; (void) CLNT_DESTROY(client); } (void) close(fd); if (failure) exit(1); return; } /* * If the version number is given, ping that (prog, vers); else try to find * the version numbers supported for that prog and ping all the versions. * Remote rpcbind is *contacted* for this service. The requests are * then sent directly to the services themselves. */ static void progping(char *netid, int argc, char **argv) { CLIENT *client; struct timeval to; enum clnt_stat rpc_stat; u_long prognum, versnum, minvers, maxvers; struct rpc_err rpcerr; int failure = 0; struct netconfig *nconf; if (argc < 2 || argc > 3 || (netid == NULL)) usage(); prognum = getprognum(argv[1]); if (argc == 2) { /* Version number not known */ /* * A call to version 0 should fail with a program/version * mismatch, and give us the range of versions supported. */ versnum = MIN_VERS; } else { versnum = getvers(argv[2]); } if (netid) { nconf = getnetconfigent(netid); if (nconf == (struct netconfig *)NULL) errx(1, "could not find %s", netid); client = clnt_tp_create(argv[0], prognum, versnum, nconf); } else { client = clnt_create(argv[0], prognum, versnum, "NETPATH"); } if (client == (CLIENT *)NULL) { clnt_pcreateerror("rpcinfo"); exit(1); } to.tv_sec = 10; to.tv_usec = 0; rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_void, (char *)NULL, to); if (argc == 3) { /* Version number was known */ if (pstatus(client, prognum, versnum) < 0) failure = 1; (void) CLNT_DESTROY(client); if (failure) exit(1); return; } /* Version number not known */ if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr(client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * Oh dear, it DOES support version 0. * Let's try version MAX_VERS. */ versnum = MAX_VERS; (void) CLNT_CONTROL(client, CLSET_VERS, (char *)&versnum); rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_void, (char *)NULL, to); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr(client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * It also supports version MAX_VERS. * Looks like we have a wise guy. * OK, we give them information on all * 4 billion versions they support... */ minvers = 0; maxvers = MAX_VERS; } else { (void) pstatus(client, prognum, MAX_VERS); exit(1); } } else { (void) pstatus(client, prognum, (u_long)0); exit(1); } for (versnum = minvers; versnum <= maxvers; versnum++) { (void) CLNT_CONTROL(client, CLSET_VERS, (char *)&versnum); rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_void, (char *)NULL, to); if (pstatus(client, prognum, versnum) < 0) failure = 1; } (void) CLNT_DESTROY(client); if (failure) exit(1); return; } static void usage(void) { fprintf(stderr, "usage: rpcinfo [-m | -s] [host]\n"); #ifdef PORTMAP fprintf(stderr, " rpcinfo -p [host]\n"); #endif fprintf(stderr, " rpcinfo -T netid host prognum [versnum]\n"); fprintf(stderr, " rpcinfo -l host prognum versnum\n"); #ifdef PORTMAP fprintf(stderr, " rpcinfo [-n portnum] -u | -t host prognum [versnum]\n"); #endif fprintf(stderr, " rpcinfo -a serv_address -T netid prognum [version]\n"); fprintf(stderr, " rpcinfo -b prognum versnum\n"); fprintf(stderr, " rpcinfo -d [-T netid] prognum versnum\n"); exit(1); } static u_long getprognum (char *arg) { char *strptr; register struct rpcent *rpc; register u_long prognum; char *tptr = arg; while (*tptr && isdigit(*tptr++)); if (*tptr || isalpha(*(tptr - 1))) { rpc = getrpcbyname(arg); if (rpc == NULL) errx(1, "%s is unknown service", arg); prognum = rpc->r_number; } else { prognum = strtol(arg, &strptr, 10); if (strptr == arg || *strptr != '\0') errx(1, "%s is illegal program number", arg); } return (prognum); } static u_long getvers(char *arg) { char *strptr; register u_long vers; vers = (int) strtol(arg, &strptr, 10); if (strptr == arg || *strptr != '\0') errx(1, "%s is illegal version number", arg); return (vers); } /* * This routine should take a pointer to an "rpc_err" structure, rather than * a pointer to a CLIENT structure, but "clnt_perror" takes a pointer to * a CLIENT structure rather than a pointer to an "rpc_err" structure. * As such, we have to keep the CLIENT structure around in order to print * a good error message. */ static int pstatus(register CLIENT *client, u_long prog, u_long vers) { struct rpc_err rpcerr; clnt_geterr(client, &rpcerr); if (rpcerr.re_status != RPC_SUCCESS) { clnt_perror(client, "rpcinfo"); printf("program %lu version %lu is not available\n", prog, vers); return (-1); } else { printf("program %lu version %lu ready and waiting\n", prog, vers); return (0); } } static CLIENT * clnt_rpcbind_create(char *host, int rpcbversnum, struct netbuf **targaddr) { static const char *tlist[3] = { "circuit_n", "circuit_v", "datagram_v" }; int i; struct netconfig *nconf; CLIENT *clnt = NULL; void *handle; rpc_createerr.cf_stat = RPC_SUCCESS; for (i = 0; i < 3; i++) { if ((handle = __rpc_setconf(tlist[i])) == NULL) continue; while (clnt == (CLIENT *)NULL) { if ((nconf = __rpc_getconf(handle)) == NULL) { if (rpc_createerr.cf_stat == RPC_SUCCESS) rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; break; } clnt = getclnthandle(host, nconf, rpcbversnum, targaddr); } if (clnt) break; __rpc_endconf(handle); } return (clnt); } static CLIENT* getclnthandle(char *host, struct netconfig *nconf, u_long rpcbversnum, struct netbuf **targaddr) { struct netbuf addr; struct addrinfo hints, *res; CLIENT *client = NULL; /* Get the address of the rpcbind */ memset(&hints, 0, sizeof hints); if (getaddrinfo(host, "rpcbind", &hints, &res) != 0) { rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE; return (NULL); } addr.len = addr.maxlen = res->ai_addrlen; addr.buf = res->ai_addr; client = clnt_tli_create(RPC_ANYFD, nconf, &addr, RPCBPROG, rpcbversnum, 0, 0); if (client) { if (targaddr != NULL) { *targaddr = (struct netbuf *)malloc(sizeof (struct netbuf)); if (*targaddr != NULL) { (*targaddr)->maxlen = addr.maxlen; (*targaddr)->len = addr.len; (*targaddr)->buf = (char *)malloc(addr.len); if ((*targaddr)->buf != NULL) { memcpy((*targaddr)->buf, addr.buf, addr.len); } } } } else { if (rpc_createerr.cf_stat == RPC_TLIERROR) { /* * Assume that the other system is dead; this is a * better error to display to the user. */ rpc_createerr.cf_stat = RPC_RPCBFAILURE; rpc_createerr.cf_error.re_status = RPC_FAILED; } } freeaddrinfo(res); return (client); } static void print_rmtcallstat(int rtype, rpcb_stat *infp) { register rpcbs_rmtcalllist_ptr pr; struct rpcent *rpc; if (rtype == RPCBVERS_4_STAT) printf( "prog\t\tvers\tproc\tnetid\tindirect success failure\n"); else printf("prog\t\tvers\tproc\tnetid\tsuccess\tfailure\n"); for (pr = infp->rmtinfo; pr; pr = pr->next) { rpc = getrpcbynumber(pr->prog); if (rpc) printf("%-16s", rpc->r_name); else printf("%-16d", pr->prog); printf("%d\t%d\t%s\t", pr->vers, pr->proc, pr->netid); if (rtype == RPCBVERS_4_STAT) printf("%d\t ", pr->indirect); printf("%d\t%d\n", pr->success, pr->failure); } } static void print_getaddrstat(int rtype, rpcb_stat *infp) { rpcbs_addrlist_ptr al; register struct rpcent *rpc; printf("prog\t\tvers\tnetid\t success\tfailure\n"); for (al = infp->addrinfo; al; al = al->next) { rpc = getrpcbynumber(al->prog); if (rpc) printf("%-16s", rpc->r_name); else printf("%-16d", al->prog); printf("%d\t%s\t %-12d\t%d\n", al->vers, al->netid, al->success, al->failure); } } static char * spaces(int howmany) { static char space_array[] = /* 64 spaces */ " "; if (howmany <= 0 || howmany > sizeof (space_array)) { return (""); } return (&space_array[sizeof (space_array) - howmany - 1]); } diff --git a/usr.bin/sdiff/edit.c b/usr.bin/sdiff/edit.c index af49fd16a2ad..249cb09ed44e 100644 --- a/usr.bin/sdiff/edit.c +++ b/usr.bin/sdiff/edit.c @@ -1,209 +1,208 @@ /* $OpenBSD: edit.c,v 1.19 2009/06/07 13:29:50 ray Exp $ */ /* * Written by Raymond Lai . * Public domain. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" static void cleanup(const char *filename) { if (unlink(filename)) err(2, "could not delete: %s", filename); exit(2); } /* * Execute an editor on the specified pathname, which is interpreted * from the shell. This means flags may be included. * * Returns -1 on error, or the exit value on success. */ static int editit(const char *pathname) { sig_t sighup, sigint, sigquit, sigchld; pid_t pid; int saved_errno, st, ret = -1; const char *ed; ed = getenv("VISUAL"); if (ed == NULL) ed = getenv("EDITOR"); if (ed == NULL) ed = _PATH_VI; sighup = signal(SIGHUP, SIG_IGN); sigint = signal(SIGINT, SIG_IGN); sigquit = signal(SIGQUIT, SIG_IGN); sigchld = signal(SIGCHLD, SIG_DFL); if ((pid = fork()) == -1) goto fail; if (pid == 0) { execlp(ed, ed, pathname, (char *)NULL); _exit(127); } while (waitpid(pid, &st, 0) == -1) if (errno != EINTR) goto fail; if (!WIFEXITED(st)) errno = EINTR; else ret = WEXITSTATUS(st); fail: saved_errno = errno; (void)signal(SIGHUP, sighup); (void)signal(SIGINT, sigint); (void)signal(SIGQUIT, sigquit); (void)signal(SIGCHLD, sigchld); errno = saved_errno; return (ret); } /* * Parse edit command. Returns 0 on success, -1 on error. */ int eparse(const char *cmd, const char *left, const char *right) { FILE *file; size_t nread; int fd; char *filename; char buf[BUFSIZ], *text; /* Skip whitespace. */ while (isspace(*cmd)) ++cmd; text = NULL; switch (*cmd) { case '\0': /* Edit empty file. */ break; case 'b': /* Both strings. */ if (left == NULL) goto RIGHT; if (right == NULL) goto LEFT; /* Neither column is blank, so print both. */ if (asprintf(&text, "%s\n%s\n", left, right) == -1) err(2, "could not allocate memory"); break; case 'l': LEFT: /* Skip if there is no left column. */ if (left == NULL) break; if (asprintf(&text, "%s\n", left) == -1) err(2, "could not allocate memory"); break; case 'r': RIGHT: /* Skip if there is no right column. */ if (right == NULL) break; if (asprintf(&text, "%s\n", right) == -1) err(2, "could not allocate memory"); break; default: return (-1); } /* Create temp file. */ if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1) err(2, "asprintf"); if ((fd = mkstemp(filename)) == -1) err(2, "mkstemp"); if (text != NULL) { size_t len; ssize_t nwritten; len = strlen(text); if ((nwritten = write(fd, text, len)) == -1 || (size_t)nwritten != len) { warn("error writing to temp file"); cleanup(filename); } } close(fd); /* text is no longer used. */ free(text); /* Edit temp file. */ if (editit(filename) == -1) { warn("error editing %s", filename); cleanup(filename); } /* Open temporary file. */ if (!(file = fopen(filename, "r"))) { warn("could not open edited file: %s", filename); cleanup(filename); } /* Copy temporary file contents to output file. */ for (nread = sizeof(buf); nread == sizeof(buf);) { size_t nwritten; nread = fread(buf, sizeof(*buf), sizeof(buf), file); /* Test for error or end of file. */ if (nread != sizeof(buf) && (ferror(file) || !feof(file))) { warnx("error reading edited file: %s", filename); cleanup(filename); } /* * If we have nothing to read, break out of loop * instead of writing nothing. */ if (!nread) break; /* Write data we just read. */ nwritten = fwrite(buf, sizeof(*buf), nread, outfp); if (nwritten != nread) { warnx("error writing to output file"); cleanup(filename); } } /* We've reached the end of the temporary file, so remove it. */ if (unlink(filename)) warn("could not delete: %s", filename); fclose(file); free(filename); return (0); } diff --git a/usr.bin/sdiff/sdiff.c b/usr.bin/sdiff/sdiff.c index b863d5875db6..f177f11e9156 100644 --- a/usr.bin/sdiff/sdiff.c +++ b/usr.bin/sdiff/sdiff.c @@ -1,1157 +1,1156 @@ /* $OpenBSD: sdiff.c,v 1.36 2015/12/29 19:04:46 gsoares Exp $ */ /* * Written by Raymond Lai . * Public domain. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" static char diff_path[] = "/usr/bin/diff"; #define WIDTH 126 /* * Each column must be at least one character wide, plus three * characters between the columns (space, [<|>], space). */ #define WIDTH_MIN 5 /* 3 kilobytes of chars */ #define MAX_CHECK 768 /* A single diff line. */ struct diffline { STAILQ_ENTRY(diffline) diffentries; char *left; char div; char *right; }; static void astrcat(char **, const char *); static void enqueue(char *, char, char *); static char *mktmpcpy(const char *); static int istextfile(FILE *); static void binexec(char *, char *, char *) __dead2; static void freediff(struct diffline *); static void int_usage(void); static int parsecmd(FILE *, FILE *, FILE *); static void printa(FILE *, size_t); static void printc(FILE *, size_t, FILE *, size_t); static void printcol(const char *, size_t *, const size_t); static void printd(FILE *, size_t); static void println(const char *, const char, const char *); static void processq(void); static void prompt(const char *, const char *); static void usage(void) __dead2; static char *xfgets(FILE *); static STAILQ_HEAD(, diffline) diffhead = STAILQ_HEAD_INITIALIZER(diffhead); static size_t line_width; /* width of a line (two columns and divider) */ static size_t width; /* width of each column */ static size_t file1ln, file2ln; /* line number of file1 and file2 */ static int Iflag = 0; /* ignore sets matching regexp */ static int lflag; /* print only left column for identical lines */ static int sflag; /* skip identical lines */ FILE *outfp; /* file to save changes to */ const char *tmpdir; /* TMPDIR or /tmp */ enum { HELP_OPT = CHAR_MAX + 1, NORMAL_OPT, FCASE_SENSITIVE_OPT, FCASE_IGNORE_OPT, STRIPCR_OPT, TSIZE_OPT, DIFFPROG_OPT, }; static struct option longopts[] = { /* options only processed in sdiff */ { "suppress-common-lines", no_argument, NULL, 's' }, { "width", required_argument, NULL, 'w' }, { "output", required_argument, NULL, 'o' }, { "diff-program", required_argument, NULL, DIFFPROG_OPT }, /* Options processed by diff. */ { "ignore-file-name-case", no_argument, NULL, FCASE_IGNORE_OPT }, { "no-ignore-file-name-case", no_argument, NULL, FCASE_SENSITIVE_OPT }, { "strip-trailing-cr", no_argument, NULL, STRIPCR_OPT }, { "tabsize", required_argument, NULL, TSIZE_OPT }, { "help", no_argument, NULL, HELP_OPT }, { "text", no_argument, NULL, 'a' }, { "ignore-blank-lines", no_argument, NULL, 'B' }, { "ignore-space-change", no_argument, NULL, 'b' }, { "minimal", no_argument, NULL, 'd' }, { "ignore-tab-expansion", no_argument, NULL, 'E' }, { "ignore-matching-lines", required_argument, NULL, 'I' }, { "ignore-case", no_argument, NULL, 'i' }, { "left-column", no_argument, NULL, 'l' }, { "expand-tabs", no_argument, NULL, 't' }, { "speed-large-files", no_argument, NULL, 'H' }, { "ignore-all-space", no_argument, NULL, 'W' }, { NULL, 0, NULL, '\0'} }; static const char *help_msg[] = { "usage: sdiff [-abdilstW] [-I regexp] [-o outfile] [-w width] file1 file2\n", "-l, --left-column: only print the left column for identical lines.", "-o OUTFILE, --output=OUTFILE: interactively merge file1 and file2 into outfile.", "-s, --suppress-common-lines: skip identical lines.", "-w WIDTH, --width=WIDTH: print a maximum of WIDTH characters on each line.", "", "Options passed to diff(1) are:", "\t-a, --text: treat file1 and file2 as text files.", "\t-b, --ignore-trailing-cr: ignore trailing blank spaces.", "\t-d, --minimal: minimize diff size.", "\t-I RE, --ignore-matching-lines=RE: ignore changes whose line matches RE.", "\t-i, --ignore-case: do a case-insensitive comparison.", "\t-t, --expand-tabs: sxpand tabs to spaces.", "\t-W, --ignore-all-spaces: ignore all spaces.", "\t--speed-large-files: assume large file with scattered changes.", "\t--strip-trailing-cr: strip trailing carriage return.", "\t--ignore-file-name-case: ignore case of file names.", "\t--no-ignore-file-name-case: do not ignore file name case", "\t--tabsize NUM: change size of tabs (default 8.)", NULL, }; /* * Create temporary file if source_file is not a regular file. * Returns temporary file name if one was malloced, NULL if unnecessary. */ static char * mktmpcpy(const char *source_file) { struct stat sb; ssize_t rcount; int ifd, ofd; u_char buf[BUFSIZ]; char *target_file; /* Open input and output. */ ifd = open(source_file, O_RDONLY, 0); /* File was opened successfully. */ if (ifd != -1) { if (fstat(ifd, &sb) == -1) err(2, "error getting file status from %s", source_file); /* Regular file. */ if (S_ISREG(sb.st_mode)) { close(ifd); return (NULL); } } else { /* If ``-'' does not exist the user meant stdin. */ if (errno == ENOENT && strcmp(source_file, "-") == 0) ifd = STDIN_FILENO; else err(2, "error opening %s", source_file); } /* Not a regular file, so copy input into temporary file. */ if (asprintf(&target_file, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1) err(2, "asprintf"); if ((ofd = mkstemp(target_file)) == -1) { warn("error opening %s", target_file); goto FAIL; } while ((rcount = read(ifd, buf, sizeof(buf))) != -1 && rcount != 0) { ssize_t wcount; wcount = write(ofd, buf, (size_t)rcount); if (-1 == wcount || rcount != wcount) { warn("error writing to %s", target_file); goto FAIL; } } if (rcount == -1) { warn("error reading from %s", source_file); goto FAIL; } close(ifd); close(ofd); return (target_file); FAIL: unlink(target_file); exit(2); } int main(int argc, char **argv) { FILE *diffpipe=NULL, *file1, *file2; size_t diffargc = 0, wflag = WIDTH; int ch, fd[2] = {-1}, status; pid_t pid=0; const char *outfile = NULL; char **diffargv, *diffprog = diff_path, *filename1, *filename2, *tmp1, *tmp2, *s1, *s2; int i; char I_arg[] = "-I"; char speed_lf[] = "--speed-large-files"; /* * Process diff flags. */ /* * Allocate memory for diff arguments and NULL. * Each flag has at most one argument, so doubling argc gives an * upper limit of how many diff args can be passed. argv[0], * file1, and file2 won't have arguments so doubling them will * waste some memory; however we need an extra space for the * NULL at the end, so it sort of works out. */ if (!(diffargv = calloc(argc, sizeof(char **) * 2))) err(2, "main"); /* Add first argument, the program name. */ diffargv[diffargc++] = diffprog; /* create a dynamic string for merging single-switch options */ if ( asprintf(&diffargv[diffargc++], "-") < 0 ) err(2, "main"); while ((ch = getopt_long(argc, argv, "aBbdEHI:ilo:stWw:", longopts, NULL)) != -1) { const char *errstr; switch (ch) { /* only compatible --long-name-form with diff */ case FCASE_IGNORE_OPT: case FCASE_SENSITIVE_OPT: case STRIPCR_OPT: case TSIZE_OPT: case 'S': break; /* combine no-arg single switches */ case 'a': case 'B': case 'b': case 'd': case 'E': case 'i': case 't': case 'W': diffargv[1] = realloc(diffargv[1], sizeof(char) * strlen(diffargv[1]) + 2); /* * In diff, the 'W' option is 'w' and the 'w' is 'W'. */ if (ch == 'W') sprintf(diffargv[1], "%sw", diffargv[1]); else sprintf(diffargv[1], "%s%c", diffargv[1], ch); break; case 'H': diffargv[diffargc++] = speed_lf; break; case DIFFPROG_OPT: diffargv[0] = diffprog = optarg; break; case 'I': Iflag = 1; diffargv[diffargc++] = I_arg; diffargv[diffargc++] = optarg; break; case 'l': lflag = 1; break; case 'o': outfile = optarg; break; case 's': sflag = 1; break; case 'w': wflag = strtonum(optarg, WIDTH_MIN, INT_MAX, &errstr); if (errstr) errx(2, "width is %s: %s", errstr, optarg); break; case HELP_OPT: for (i = 0; help_msg[i] != NULL; i++) printf("%s\n", help_msg[i]); exit(0); break; default: usage(); break; } } /* no single switches were used */ if (strcmp(diffargv[1], "-") == 0 ) { for ( i = 1; i < argc-1; i++) { diffargv[i] = diffargv[i+1]; } diffargv[diffargc-1] = NULL; diffargc--; } argc -= optind; argv += optind; if (argc != 2) usage(); if (outfile && (outfp = fopen(outfile, "w")) == NULL) err(2, "could not open: %s", optarg); if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0') tmpdir = _PATH_TMP; filename1 = argv[0]; filename2 = argv[1]; /* * Create temporary files for diff and sdiff to share if file1 * or file2 are not regular files. This allows sdiff and diff * to read the same inputs if one or both inputs are stdin. * * If any temporary files were created, their names would be * saved in tmp1 or tmp2. tmp1 should never equal tmp2. */ tmp1 = tmp2 = NULL; /* file1 and file2 are the same, so copy to same temp file. */ if (strcmp(filename1, filename2) == 0) { if ((tmp1 = mktmpcpy(filename1))) filename1 = filename2 = tmp1; /* Copy file1 and file2 into separate temp files. */ } else { if ((tmp1 = mktmpcpy(filename1))) filename1 = tmp1; if ((tmp2 = mktmpcpy(filename2))) filename2 = tmp2; } diffargv[diffargc++] = filename1; diffargv[diffargc++] = filename2; /* Add NULL to end of array to indicate end of array. */ diffargv[diffargc++] = NULL; /* Subtract column divider and divide by two. */ width = (wflag - 3) / 2; /* Make sure line_width can fit in size_t. */ if (width > (SIZE_MAX - 3) / 2) errx(2, "width is too large: %zu", width); line_width = width * 2 + 3; if (pipe(fd)) err(2, "pipe"); switch (pid = fork()) { case 0: /* child */ /* We don't read from the pipe. */ close(fd[0]); if (dup2(fd[1], STDOUT_FILENO) == -1) err(2, "child could not duplicate descriptor"); /* Free unused descriptor. */ close(fd[1]); execvp(diffprog, diffargv); err(2, "could not execute diff: %s", diffprog); break; case -1: err(2, "could not fork"); break; } /* parent */ /* We don't write to the pipe. */ close(fd[1]); /* Open pipe to diff command. */ if ((diffpipe = fdopen(fd[0], "r")) == NULL) err(2, "could not open diff pipe"); if ((file1 = fopen(filename1, "r")) == NULL) err(2, "could not open %s", filename1); if ((file2 = fopen(filename2, "r")) == NULL) err(2, "could not open %s", filename2); if (!istextfile(file1) || !istextfile(file2)) { /* Close open files and pipe, delete temps */ fclose(file1); fclose(file2); if (diffpipe != NULL) fclose(diffpipe); if (tmp1) if (unlink(tmp1)) warn("Error deleting %s.", tmp1); if (tmp2) if (unlink(tmp2)) warn("Error deleting %s.", tmp2); free(tmp1); free(tmp2); binexec(diffprog, filename1, filename2); } /* Line numbers start at one. */ file1ln = file2ln = 1; /* Read and parse diff output. */ while (parsecmd(diffpipe, file1, file2) != EOF) ; fclose(diffpipe); /* Wait for diff to exit. */ if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) || WEXITSTATUS(status) >= 2) err(2, "diff exited abnormally."); /* Delete and free unneeded temporary files. */ if (tmp1) if (unlink(tmp1)) warn("Error deleting %s.", tmp1); if (tmp2) if (unlink(tmp2)) warn("Error deleting %s.", tmp2); free(tmp1); free(tmp2); filename1 = filename2 = tmp1 = tmp2 = NULL; /* No more diffs, so print common lines. */ if (lflag) while ((s1 = xfgets(file1))) enqueue(s1, ' ', NULL); else for (;;) { s1 = xfgets(file1); s2 = xfgets(file2); if (s1 || s2) enqueue(s1, ' ', s2); else break; } fclose(file1); fclose(file2); /* Process unmodified lines. */ processq(); /* Return diff exit status. */ return (WEXITSTATUS(status)); } /* * When sdiff detects a binary file as input, executes them with * diff to maintain the same behavior as GNU sdiff with binary input. */ static void binexec(char *diffprog, char *f1, char *f2) { char *args[] = {diffprog, f1, f2, (char *) 0}; execv(diffprog, args); /* If execv() fails, sdiff's execution will continue below. */ errx(1, "could not execute diff process"); } /* * Checks whether a file appears to be a text file. */ static int istextfile(FILE *f) { int ch, i; if (f == NULL) return (1); rewind(f); for (i = 0; i <= MAX_CHECK; i++) { ch = fgetc(f); if (ch == '\0') { rewind(f); return (0); } if (ch == EOF) break; } rewind(f); return (1); } /* * Prints an individual column (left or right), taking into account * that tabs are variable-width. Takes a string, the current column * the cursor is on the screen, and the maximum value of the column. * The column value is updated as we go along. */ static void printcol(const char *s, size_t *col, const size_t col_max) { for (; *s && *col < col_max; ++s) { size_t new_col; switch (*s) { case '\t': /* * If rounding to next multiple of eight causes * an integer overflow, just return. */ if (*col > SIZE_MAX - 8) return; /* Round to next multiple of eight. */ new_col = (*col / 8 + 1) * 8; /* * If printing the tab goes past the column * width, don't print it and just quit. */ if (new_col > col_max) return; *col = new_col; break; default: ++(*col); } putchar(*s); } } /* * Prompts user to either choose between two strings or edit one, both, * or neither. */ static void prompt(const char *s1, const char *s2) { char *cmd; /* Print command prompt. */ putchar('%'); /* Get user input. */ for (; (cmd = xfgets(stdin)); free(cmd)) { const char *p; /* Skip leading whitespace. */ for (p = cmd; isspace(*p); ++p) ; switch (*p) { case 'e': /* Skip `e'. */ ++p; if (eparse(p, s1, s2) == -1) goto USAGE; break; case 'l': case '1': /* Choose left column as-is. */ if (s1 != NULL) fprintf(outfp, "%s\n", s1); /* End of command parsing. */ break; case 'q': goto QUIT; case 'r': case '2': /* Choose right column as-is. */ if (s2 != NULL) fprintf(outfp, "%s\n", s2); /* End of command parsing. */ break; case 's': sflag = 1; goto PROMPT; case 'v': sflag = 0; /* FALLTHROUGH */ default: /* Interactive usage help. */ USAGE: int_usage(); PROMPT: putchar('%'); /* Prompt user again. */ continue; } free(cmd); return; } /* * If there was no error, we received an EOF from stdin, so we * should quit. */ QUIT: fclose(outfp); exit(0); } /* * Takes two strings, separated by a column divider. NULL strings are * treated as empty columns. If the divider is the ` ' character, the * second column is not printed (-l flag). In this case, the second * string must be NULL. When the second column is NULL, the divider * does not print the trailing space following the divider character. * * Takes into account that tabs can take multiple columns. */ static void println(const char *s1, const char divider, const char *s2) { size_t col; /* Print first column. Skips if s1 == NULL. */ col = 0; if (s1) { /* Skip angle bracket and space. */ printcol(s1, &col, width); } /* Otherwise, we pad this column up to width. */ for (; col < width; ++col) putchar(' '); /* Only print left column. */ if (divider == ' ' && !s2) { printf(" (\n"); return; } /* * Print column divider. If there is no second column, we don't * need to add the space for padding. */ if (!s2) { printf(" %c\n", divider); return; } printf(" %c ", divider); col += 3; /* Skip angle bracket and space. */ printcol(s2, &col, line_width); putchar('\n'); } /* * Reads a line from file and returns as a string. If EOF is reached, * NULL is returned. The returned string must be freed afterwards. */ static char * xfgets(FILE *file) { size_t linecap; ssize_t l; char *s; clearerr(file); linecap = 0; s = NULL; if ((l = getline(&s, &linecap, file)) == -1) { if (ferror(file)) err(2, "error reading file"); return (NULL); } if (s[l-1] == '\n') s[l-1] = '\0'; return (s); } /* * Parse ed commands from diffpipe and print lines from file1 (lines * to change or delete) or file2 (lines to add or change). * Returns EOF or 0. */ static int parsecmd(FILE *diffpipe, FILE *file1, FILE *file2) { size_t file1start, file1end, file2start, file2end, n; /* ed command line and pointer to characters in line */ char *line, *p, *q; const char *errstr; char c, cmd; /* Read ed command. */ if (!(line = xfgets(diffpipe))) return (EOF); p = line; /* Go to character after line number. */ while (isdigit(*p)) ++p; c = *p; *p++ = 0; file1start = strtonum(line, 0, INT_MAX, &errstr); if (errstr) errx(2, "file1 start is %s: %s", errstr, line); /* A range is specified for file1. */ if (c == ',') { q = p; /* Go to character after file2end. */ while (isdigit(*p)) ++p; c = *p; *p++ = 0; file1end = strtonum(q, 0, INT_MAX, &errstr); if (errstr) errx(2, "file1 end is %s: %s", errstr, line); if (file1start > file1end) errx(2, "invalid line range in file1: %s", line); } else file1end = file1start; cmd = c; /* Check that cmd is valid. */ if (!(cmd == 'a' || cmd == 'c' || cmd == 'd')) errx(2, "ed command not recognized: %c: %s", cmd, line); q = p; /* Go to character after line number. */ while (isdigit(*p)) ++p; c = *p; *p++ = 0; file2start = strtonum(q, 0, INT_MAX, &errstr); if (errstr) errx(2, "file2 start is %s: %s", errstr, line); /* * There should either be a comma signifying a second line * number or the line should just end here. */ if (c != ',' && c != '\0') errx(2, "invalid line range in file2: %c: %s", c, line); if (c == ',') { file2end = strtonum(p, 0, INT_MAX, &errstr); if (errstr) errx(2, "file2 end is %s: %s", errstr, line); if (file2start >= file2end) errx(2, "invalid line range in file2: %s", line); } else file2end = file2start; /* Appends happen _after_ stated line. */ if (cmd == 'a') { if (file1start != file1end) errx(2, "append cannot have a file1 range: %s", line); if (file1start == SIZE_MAX) errx(2, "file1 line range too high: %s", line); file1start = ++file1end; } /* * I'm not sure what the deal is with the line numbers for * deletes, though. */ else if (cmd == 'd') { if (file2start != file2end) errx(2, "delete cannot have a file2 range: %s", line); if (file2start == SIZE_MAX) errx(2, "file2 line range too high: %s", line); file2start = ++file2end; } /* * Continue reading file1 and file2 until we reach line numbers * specified by diff. Should only happen with -I flag. */ for (; file1ln < file1start && file2ln < file2start; ++file1ln, ++file2ln) { char *s1, *s2; if (!(s1 = xfgets(file1))) errx(2, "file1 shorter than expected"); if (!(s2 = xfgets(file2))) errx(2, "file2 shorter than expected"); /* If the -l flag was specified, print only left column. */ if (lflag) { free(s2); /* * XXX - If -l and -I are both specified, all * unchanged or ignored lines are shown with a * `(' divider. This matches GNU sdiff, but I * believe it is a bug. Just check out: * gsdiff -l -I '^$' samefile samefile. */ if (Iflag) enqueue(s1, '(', NULL); else enqueue(s1, ' ', NULL); } else enqueue(s1, ' ', s2); } /* Ignore deleted lines. */ for (; file1ln < file1start; ++file1ln) { char *s; if (!(s = xfgets(file1))) errx(2, "file1 shorter than expected"); enqueue(s, '(', NULL); } /* Ignore added lines. */ for (; file2ln < file2start; ++file2ln) { char *s; if (!(s = xfgets(file2))) errx(2, "file2 shorter than expected"); /* If -l flag was given, don't print right column. */ if (lflag) free(s); else enqueue(NULL, ')', s); } /* Process unmodified or skipped lines. */ processq(); switch (cmd) { case 'a': printa(file2, file2end); n = file2end - file2start + 1; break; case 'c': printc(file1, file1end, file2, file2end); n = file1end - file1start + 1 + 1 + file2end - file2start + 1; break; case 'd': printd(file1, file1end); n = file1end - file1start + 1; break; default: errx(2, "invalid diff command: %c: %s", cmd, line); } free(line); /* Skip to next ed line. */ while (n--) { if (!(line = xfgets(diffpipe))) errx(2, "diff ended early"); free(line); } return (0); } /* * Queues up a diff line. */ static void enqueue(char *left, char divider, char *right) { struct diffline *diffp; if (!(diffp = malloc(sizeof(struct diffline)))) err(2, "enqueue"); diffp->left = left; diffp->div = divider; diffp->right = right; STAILQ_INSERT_TAIL(&diffhead, diffp, diffentries); } /* * Free a diffline structure and its elements. */ static void freediff(struct diffline *diffp) { free(diffp->left); free(diffp->right); free(diffp); } /* * Append second string into first. Repeated appends to the same string * are cached, making this an O(n) function, where n = strlen(append). */ static void astrcat(char **s, const char *append) { /* Length of string in previous run. */ static size_t offset = 0; size_t newsiz; /* * String from previous run. Compared to *s to see if we are * dealing with the same string. If so, we can use offset. */ static const char *oldstr = NULL; char *newstr; /* * First string is NULL, so just copy append. */ if (!*s) { if (!(*s = strdup(append))) err(2, "astrcat"); /* Keep track of string. */ offset = strlen(*s); oldstr = *s; return; } /* * *s is a string so concatenate. */ /* Did we process the same string in the last run? */ /* * If this is a different string from the one we just processed * cache new string. */ if (oldstr != *s) { offset = strlen(*s); oldstr = *s; } /* Size = strlen(*s) + \n + strlen(append) + '\0'. */ newsiz = offset + 1 + strlen(append) + 1; /* Resize *s to fit new string. */ newstr = realloc(*s, newsiz); if (newstr == NULL) err(2, "astrcat"); *s = newstr; /* *s + offset should be end of string. */ /* Concatenate. */ strlcpy(*s + offset, "\n", newsiz - offset); strlcat(*s + offset, append, newsiz - offset); /* New string length should be exactly newsiz - 1 characters. */ /* Store generated string's values. */ offset = newsiz - 1; oldstr = *s; } /* * Process diff set queue, printing, prompting, and saving each diff * line stored in queue. */ static void processq(void) { struct diffline *diffp; char divc, *left, *right; /* Don't process empty queue. */ if (STAILQ_EMPTY(&diffhead)) return; /* Remember the divider. */ divc = STAILQ_FIRST(&diffhead)->div; left = NULL; right = NULL; /* * Go through set of diffs, concatenating each line in left or * right column into two long strings, `left' and `right'. */ STAILQ_FOREACH(diffp, &diffhead, diffentries) { /* * Print changed lines if -s was given, * print all lines if -s was not given. */ if (!sflag || diffp->div == '|' || diffp->div == '<' || diffp->div == '>') println(diffp->left, diffp->div, diffp->right); /* Append new lines to diff set. */ if (diffp->left) astrcat(&left, diffp->left); if (diffp->right) astrcat(&right, diffp->right); } /* Empty queue and free each diff line and its elements. */ while (!STAILQ_EMPTY(&diffhead)) { diffp = STAILQ_FIRST(&diffhead); STAILQ_REMOVE_HEAD(&diffhead, diffentries); freediff(diffp); } /* Write to outfp, prompting user if lines are different. */ if (outfp) switch (divc) { case ' ': case '(': case ')': fprintf(outfp, "%s\n", left); break; case '|': case '<': case '>': prompt(left, right); break; default: errx(2, "invalid divider: %c", divc); } /* Free left and right. */ free(left); free(right); } /* * Print lines following an (a)ppend command. */ static void printa(FILE *file, size_t line2) { char *line; for (; file2ln <= line2; ++file2ln) { if (!(line = xfgets(file))) errx(2, "append ended early"); enqueue(NULL, '>', line); } processq(); } /* * Print lines following a (c)hange command, from file1ln to file1end * and from file2ln to file2end. */ static void printc(FILE *file1, size_t file1end, FILE *file2, size_t file2end) { struct fileline { STAILQ_ENTRY(fileline) fileentries; char *line; }; STAILQ_HEAD(, fileline) delqhead = STAILQ_HEAD_INITIALIZER(delqhead); /* Read lines to be deleted. */ for (; file1ln <= file1end; ++file1ln) { struct fileline *linep; char *line1; /* Read lines from both. */ if (!(line1 = xfgets(file1))) errx(2, "error reading file1 in delete in change"); /* Add to delete queue. */ if (!(linep = malloc(sizeof(struct fileline)))) err(2, "printc"); linep->line = line1; STAILQ_INSERT_TAIL(&delqhead, linep, fileentries); } /* Process changed lines.. */ for (; !STAILQ_EMPTY(&delqhead) && file2ln <= file2end; ++file2ln) { struct fileline *del; char *add; /* Get add line. */ if (!(add = xfgets(file2))) errx(2, "error reading add in change"); del = STAILQ_FIRST(&delqhead); enqueue(del->line, '|', add); STAILQ_REMOVE_HEAD(&delqhead, fileentries); /* * Free fileline structure but not its elements since * they are queued up. */ free(del); } processq(); /* Process remaining lines to add. */ for (; file2ln <= file2end; ++file2ln) { char *add; /* Get add line. */ if (!(add = xfgets(file2))) errx(2, "error reading add in change"); enqueue(NULL, '>', add); } processq(); /* Process remaining lines to delete. */ while (!STAILQ_EMPTY(&delqhead)) { struct fileline *filep; filep = STAILQ_FIRST(&delqhead); enqueue(filep->line, '<', NULL); STAILQ_REMOVE_HEAD(&delqhead, fileentries); free(filep); } processq(); } /* * Print deleted lines from file, from file1ln to file1end. */ static void printd(FILE *file1, size_t file1end) { char *line1; /* Print out lines file1ln to line2. */ for (; file1ln <= file1end; ++file1ln) { if (!(line1 = xfgets(file1))) errx(2, "file1 ended early in delete"); enqueue(line1, '<', NULL); } processq(); } /* * Interactive mode usage. */ static void int_usage(void) { puts("e:\tedit blank diff\n" "eb:\tedit both diffs concatenated\n" "el:\tedit left diff\n" "er:\tedit right diff\n" "l | 1:\tchoose left diff\n" "r | 2:\tchoose right diff\n" "s:\tsilent mode--don't print identical lines\n" "v:\tverbose mode--print identical lines\n" "q:\tquit"); } static void usage(void) { fprintf(stderr, "usage: sdiff [-abdilstHW] [-I regexp] [-o outfile] [-w width] file1" " file2\n"); exit(2); } diff --git a/usr.bin/sockstat/sockstat.c b/usr.bin/sockstat/sockstat.c index 52c494f03045..73b1f00a4481 100644 --- a/usr.bin/sockstat/sockstat.c +++ b/usr.bin/sockstat/sockstat.c @@ -1,1579 +1,1578 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2002 Dag-Erling Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TCPSTATES /* load state names */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define sstosin(ss) ((struct sockaddr_in *)(ss)) #define sstosin6(ss) ((struct sockaddr_in6 *)(ss)) #define sstosun(ss) ((struct sockaddr_un *)(ss)) #define sstosa(ss) ((struct sockaddr *)(ss)) static int opt_4; /* Show IPv4 sockets */ static int opt_6; /* Show IPv6 sockets */ static int opt_C; /* Show congestion control */ static int opt_c; /* Show connected sockets */ static int opt_i; /* Show inp_gencnt */ static int opt_j; /* Show specified jail */ static int opt_L; /* Don't show IPv4 or IPv6 loopback sockets */ static int opt_l; /* Show listening sockets */ static int opt_n; /* Don't resolve UIDs to user names */ static int opt_q; /* Don't show header */ static int opt_S; /* Show protocol stack if applicable */ static int opt_s; /* Show protocol state if applicable */ static int opt_U; /* Show remote UDP encapsulation port number */ static int opt_u; /* Show Unix domain sockets */ static int opt_v; /* Verbose mode */ static int opt_w; /* Wide print area for addresses */ /* * Default protocols to use if no -P was defined. */ static const char *default_protos[] = {"sctp", "tcp", "udp", "divert" }; static size_t default_numprotos = nitems(default_protos); static int *protos; /* protocols to use */ static size_t numprotos; /* allocated size of protos[] */ static int *ports; #define INT_BIT (sizeof(int)*CHAR_BIT) #define SET_PORT(p) do { ports[p / INT_BIT] |= 1 << (p % INT_BIT); } while (0) #define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT))) struct addr { union { struct sockaddr_storage address; struct { /* unix(4) faddr */ kvaddr_t conn; kvaddr_t firstref; kvaddr_t nextref; }; }; unsigned int encaps_port; int state; struct addr *next; }; struct sock { union { RB_ENTRY(sock) socket_tree; /* tree of pcbs with socket */ SLIST_ENTRY(sock) socket_list; /* list of pcbs w/o socket */ }; RB_ENTRY(sock) pcb_tree; kvaddr_t socket; kvaddr_t pcb; uint64_t inp_gencnt; int shown; int vflag; int family; int proto; int state; const char *protoname; char stack[TCP_FUNCTION_NAME_LEN_MAX]; char cc[TCP_CA_NAME_MAX]; struct addr *laddr; struct addr *faddr; }; static RB_HEAD(socks_t, sock) socks = RB_INITIALIZER(&socks); static int64_t socket_compare(const struct sock *a, const struct sock *b) { return ((int64_t)(a->socket/2 - b->socket/2)); } RB_GENERATE_STATIC(socks_t, sock, socket_tree, socket_compare); static RB_HEAD(pcbs_t, sock) pcbs = RB_INITIALIZER(&pcbs); static int64_t pcb_compare(const struct sock *a, const struct sock *b) { return ((int64_t)(a->pcb/2 - b->pcb/2)); } RB_GENERATE_STATIC(pcbs_t, sock, pcb_tree, pcb_compare); static SLIST_HEAD(, sock) nosocks = SLIST_HEAD_INITIALIZER(&nosocks); struct file { RB_ENTRY(file) file_tree; kvaddr_t xf_data; pid_t xf_pid; uid_t xf_uid; int xf_fd; }; static RB_HEAD(files_t, file) ftree = RB_INITIALIZER(&ftree); static int64_t file_compare(const struct file *a, const struct file *b) { return ((int64_t)(a->xf_data/2 - b->xf_data/2)); } RB_GENERATE_STATIC(files_t, file, file_tree, file_compare); static struct file *files; static int nfiles; static cap_channel_t *capnet; static cap_channel_t *capnetdb; static cap_channel_t *capsysctl; static cap_channel_t *cappwd; static int xprintf(const char *fmt, ...) { va_list ap; int len; va_start(ap, fmt); len = vprintf(fmt, ap); va_end(ap); if (len < 0) err(1, "printf()"); return (len); } static bool _check_ksize(size_t received_size, size_t expected_size, const char *struct_name) { if (received_size != expected_size) { warnx("%s size mismatch: expected %zd, received %zd", struct_name, expected_size, received_size); return false; } return true; } #define check_ksize(_sz, _struct) (_check_ksize(_sz, sizeof(_struct), #_struct)) static void _enforce_ksize(size_t received_size, size_t expected_size, const char *struct_name) { if (received_size != expected_size) { errx(1, "fatal: struct %s size mismatch: expected %zd, received %zd", struct_name, expected_size, received_size); } } #define enforce_ksize(_sz, _struct) (_enforce_ksize(_sz, sizeof(_struct), #_struct)) static int get_proto_type(const char *proto) { struct protoent *pent; if (strlen(proto) == 0) return (0); if (capnetdb != NULL) pent = cap_getprotobyname(capnetdb, proto); else pent = getprotobyname(proto); if (pent == NULL) { warn("cap_getprotobyname"); return (-1); } return (pent->p_proto); } static void init_protos(int num) { int proto_count = 0; if (num > 0) { proto_count = num; } else { /* Find the maximum number of possible protocols. */ while (getprotoent() != NULL) proto_count++; endprotoent(); } if ((protos = malloc(sizeof(int) * proto_count)) == NULL) err(1, "malloc"); numprotos = proto_count; } static int parse_protos(char *protospec) { char *prot; int proto_type, proto_index; if (protospec == NULL) return (-1); init_protos(0); proto_index = 0; while ((prot = strsep(&protospec, ",")) != NULL) { if (strlen(prot) == 0) continue; proto_type = get_proto_type(prot); if (proto_type != -1) protos[proto_index++] = proto_type; } numprotos = proto_index; return (proto_index); } static void parse_ports(const char *portspec) { const char *p, *q; int port, end; if (ports == NULL) if ((ports = calloc(65536 / INT_BIT, sizeof(int))) == NULL) err(1, "calloc()"); p = portspec; while (*p != '\0') { if (!isdigit(*p)) errx(1, "syntax error in port range"); for (q = p; *q != '\0' && isdigit(*q); ++q) /* nothing */ ; for (port = 0; p < q; ++p) port = port * 10 + digittoint(*p); if (port < 0 || port > 65535) errx(1, "invalid port number"); SET_PORT(port); switch (*p) { case '-': ++p; break; case ',': ++p; /* fall through */ case '\0': default: continue; } for (q = p; *q != '\0' && isdigit(*q); ++q) /* nothing */ ; for (end = 0; p < q; ++p) end = end * 10 + digittoint(*p); if (end < port || end > 65535) errx(1, "invalid port number"); while (port++ < end) SET_PORT(port); if (*p == ',') ++p; } } static void sockaddr(struct sockaddr_storage *ss, int af, void *addr, int port) { struct sockaddr_in *sin4; struct sockaddr_in6 *sin6; bzero(ss, sizeof(*ss)); switch (af) { case AF_INET: sin4 = sstosin(ss); sin4->sin_len = sizeof(*sin4); sin4->sin_family = af; sin4->sin_port = port; sin4->sin_addr = *(struct in_addr *)addr; break; case AF_INET6: sin6 = sstosin6(ss); sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = af; sin6->sin6_port = port; sin6->sin6_addr = *(struct in6_addr *)addr; #define s6_addr16 __u6_addr.__u6_addr16 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]); sin6->sin6_addr.s6_addr16[1] = 0; } break; default: abort(); } } static void free_socket(struct sock *sock) { struct addr *cur, *next; cur = sock->laddr; while (cur != NULL) { next = cur->next; free(cur); cur = next; } cur = sock->faddr; while (cur != NULL) { next = cur->next; free(cur); cur = next; } free(sock); } static void gather_sctp(void) { struct sock *sock; struct addr *laddr, *prev_laddr, *faddr, *prev_faddr; struct xsctp_inpcb *xinpcb; struct xsctp_tcb *xstcb; struct xsctp_raddr *xraddr; struct xsctp_laddr *xladdr; const char *varname; size_t len, offset; char *buf; int vflag; int no_stcb, local_all_loopback, foreign_all_loopback; vflag = 0; if (opt_4) vflag |= INP_IPV4; if (opt_6) vflag |= INP_IPV6; varname = "net.inet.sctp.assoclist"; if (cap_sysctlbyname(capsysctl, varname, 0, &len, 0, 0) < 0) { if (errno != ENOENT) err(1, "cap_sysctlbyname()"); return; } if ((buf = (char *)malloc(len)) == NULL) { err(1, "malloc()"); return; } if (cap_sysctlbyname(capsysctl, varname, buf, &len, 0, 0) < 0) { err(1, "cap_sysctlbyname()"); free(buf); return; } xinpcb = (struct xsctp_inpcb *)(void *)buf; offset = sizeof(struct xsctp_inpcb); while ((offset < len) && (xinpcb->last == 0)) { if ((sock = calloc(1, sizeof *sock)) == NULL) err(1, "malloc()"); sock->socket = xinpcb->socket; sock->proto = IPPROTO_SCTP; sock->protoname = "sctp"; if (xinpcb->maxqlen == 0) sock->state = SCTP_CLOSED; else sock->state = SCTP_LISTEN; if (xinpcb->flags & SCTP_PCB_FLAGS_BOUND_V6) { sock->family = AF_INET6; /* * Currently there is no way to distinguish between * IPv6 only sockets or dual family sockets. * So mark it as dual socket. */ sock->vflag = INP_IPV6 | INP_IPV4; } else { sock->family = AF_INET; sock->vflag = INP_IPV4; } prev_laddr = NULL; local_all_loopback = 1; while (offset < len) { xladdr = (struct xsctp_laddr *)(void *)(buf + offset); offset += sizeof(struct xsctp_laddr); if (xladdr->last == 1) break; if ((laddr = calloc(1, sizeof(struct addr))) == NULL) err(1, "malloc()"); switch (xladdr->address.sa.sa_family) { case AF_INET: #define __IN_IS_ADDR_LOOPBACK(pina) \ ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) if (!__IN_IS_ADDR_LOOPBACK( &xladdr->address.sin.sin_addr)) local_all_loopback = 0; #undef __IN_IS_ADDR_LOOPBACK sockaddr(&laddr->address, AF_INET, &xladdr->address.sin.sin_addr, htons(xinpcb->local_port)); break; case AF_INET6: if (!IN6_IS_ADDR_LOOPBACK( &xladdr->address.sin6.sin6_addr)) local_all_loopback = 0; sockaddr(&laddr->address, AF_INET6, &xladdr->address.sin6.sin6_addr, htons(xinpcb->local_port)); break; default: errx(1, "address family %d not supported", xladdr->address.sa.sa_family); } laddr->next = NULL; if (prev_laddr == NULL) sock->laddr = laddr; else prev_laddr->next = laddr; prev_laddr = laddr; } if (sock->laddr == NULL) { if ((sock->laddr = calloc(1, sizeof(struct addr))) == NULL) err(1, "malloc()"); sock->laddr->address.ss_family = sock->family; if (sock->family == AF_INET) sock->laddr->address.ss_len = sizeof(struct sockaddr_in); else sock->laddr->address.ss_len = sizeof(struct sockaddr_in6); local_all_loopback = 0; } if ((sock->faddr = calloc(1, sizeof(struct addr))) == NULL) err(1, "malloc()"); sock->faddr->address.ss_family = sock->family; if (sock->family == AF_INET) sock->faddr->address.ss_len = sizeof(struct sockaddr_in); else sock->faddr->address.ss_len = sizeof(struct sockaddr_in6); no_stcb = 1; while (offset < len) { xstcb = (struct xsctp_tcb *)(void *)(buf + offset); offset += sizeof(struct xsctp_tcb); if (no_stcb) { if (opt_l && (sock->vflag & vflag) && (!opt_L || !local_all_loopback) && ((xinpcb->flags & SCTP_PCB_FLAGS_UDPTYPE) || (xstcb->last == 1))) { RB_INSERT(socks_t, &socks, sock); } else { free_socket(sock); } } if (xstcb->last == 1) break; no_stcb = 0; if (opt_c) { if ((sock = calloc(1, sizeof *sock)) == NULL) err(1, "malloc()"); sock->socket = xinpcb->socket; sock->proto = IPPROTO_SCTP; sock->protoname = "sctp"; sock->state = (int)xstcb->state; if (xinpcb->flags & SCTP_PCB_FLAGS_BOUND_V6) { sock->family = AF_INET6; /* * Currently there is no way to distinguish * between IPv6 only sockets or dual family * sockets. So mark it as dual socket. */ sock->vflag = INP_IPV6 | INP_IPV4; } else { sock->family = AF_INET; sock->vflag = INP_IPV4; } } prev_laddr = NULL; local_all_loopback = 1; while (offset < len) { xladdr = (struct xsctp_laddr *)(void *)(buf + offset); offset += sizeof(struct xsctp_laddr); if (xladdr->last == 1) break; if (!opt_c) continue; laddr = calloc(1, sizeof(struct addr)); if (laddr == NULL) err(1, "malloc()"); switch (xladdr->address.sa.sa_family) { case AF_INET: #define __IN_IS_ADDR_LOOPBACK(pina) \ ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) if (!__IN_IS_ADDR_LOOPBACK( &xladdr->address.sin.sin_addr)) local_all_loopback = 0; #undef __IN_IS_ADDR_LOOPBACK sockaddr(&laddr->address, AF_INET, &xladdr->address.sin.sin_addr, htons(xstcb->local_port)); break; case AF_INET6: if (!IN6_IS_ADDR_LOOPBACK( &xladdr->address.sin6.sin6_addr)) local_all_loopback = 0; sockaddr(&laddr->address, AF_INET6, &xladdr->address.sin6.sin6_addr, htons(xstcb->local_port)); break; default: errx(1, "address family %d not supported", xladdr->address.sa.sa_family); } laddr->next = NULL; if (prev_laddr == NULL) sock->laddr = laddr; else prev_laddr->next = laddr; prev_laddr = laddr; } prev_faddr = NULL; foreign_all_loopback = 1; while (offset < len) { xraddr = (struct xsctp_raddr *)(void *)(buf + offset); offset += sizeof(struct xsctp_raddr); if (xraddr->last == 1) break; if (!opt_c) continue; faddr = calloc(1, sizeof(struct addr)); if (faddr == NULL) err(1, "malloc()"); switch (xraddr->address.sa.sa_family) { case AF_INET: #define __IN_IS_ADDR_LOOPBACK(pina) \ ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) if (!__IN_IS_ADDR_LOOPBACK( &xraddr->address.sin.sin_addr)) foreign_all_loopback = 0; #undef __IN_IS_ADDR_LOOPBACK sockaddr(&faddr->address, AF_INET, &xraddr->address.sin.sin_addr, htons(xstcb->remote_port)); break; case AF_INET6: if (!IN6_IS_ADDR_LOOPBACK( &xraddr->address.sin6.sin6_addr)) foreign_all_loopback = 0; sockaddr(&faddr->address, AF_INET6, &xraddr->address.sin6.sin6_addr, htons(xstcb->remote_port)); break; default: errx(1, "address family %d not supported", xraddr->address.sa.sa_family); } faddr->encaps_port = xraddr->encaps_port; faddr->state = xraddr->state; faddr->next = NULL; if (prev_faddr == NULL) sock->faddr = faddr; else prev_faddr->next = faddr; prev_faddr = faddr; } if (opt_c) { if ((sock->vflag & vflag) && (!opt_L || !(local_all_loopback || foreign_all_loopback))) { RB_INSERT(socks_t, &socks, sock); } else { free_socket(sock); } } } xinpcb = (struct xsctp_inpcb *)(void *)(buf + offset); offset += sizeof(struct xsctp_inpcb); } free(buf); } static void gather_inet(int proto) { struct xinpgen *xig, *exig; struct xinpcb *xip; struct xtcpcb *xtp = NULL; struct xsocket *so; struct sock *sock; struct addr *laddr, *faddr; const char *varname, *protoname; size_t len, bufsize; void *buf; int retry, vflag; vflag = 0; if (opt_4) vflag |= INP_IPV4; if (opt_6) vflag |= INP_IPV6; switch (proto) { case IPPROTO_TCP: varname = "net.inet.tcp.pcblist"; protoname = "tcp"; break; case IPPROTO_UDP: varname = "net.inet.udp.pcblist"; protoname = "udp"; break; case IPPROTO_DIVERT: varname = "net.inet.divert.pcblist"; protoname = "div"; break; default: errx(1, "protocol %d not supported", proto); } buf = NULL; bufsize = 8192; retry = 5; do { for (;;) { if ((buf = realloc(buf, bufsize)) == NULL) err(1, "realloc()"); len = bufsize; if (cap_sysctlbyname(capsysctl, varname, buf, &len, NULL, 0) == 0) break; if (errno == ENOENT) goto out; if (errno != ENOMEM || len != bufsize) err(1, "cap_sysctlbyname()"); bufsize *= 2; } xig = (struct xinpgen *)buf; exig = (struct xinpgen *)(void *) ((char *)buf + len - sizeof *exig); enforce_ksize(xig->xig_len, struct xinpgen); enforce_ksize(exig->xig_len, struct xinpgen); } while (xig->xig_gen != exig->xig_gen && retry--); if (xig->xig_gen != exig->xig_gen && opt_v) warnx("warning: data may be inconsistent"); for (;;) { xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len); if (xig >= exig) break; switch (proto) { case IPPROTO_TCP: xtp = (struct xtcpcb *)xig; xip = &xtp->xt_inp; if (!check_ksize(xtp->xt_len, struct xtcpcb)) goto out; protoname = xtp->t_flags & TF_TOE ? "toe" : "tcp"; break; case IPPROTO_UDP: case IPPROTO_DIVERT: xip = (struct xinpcb *)xig; if (!check_ksize(xip->xi_len, struct xinpcb)) goto out; break; default: errx(1, "protocol %d not supported", proto); } so = &xip->xi_socket; if ((xip->inp_vflag & vflag) == 0) continue; if (xip->inp_vflag & INP_IPV4) { if ((xip->inp_fport == 0 && !opt_l) || (xip->inp_fport != 0 && !opt_c)) continue; #define __IN_IS_ADDR_LOOPBACK(pina) \ ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) if (opt_L && (__IN_IS_ADDR_LOOPBACK(&xip->inp_faddr) || __IN_IS_ADDR_LOOPBACK(&xip->inp_laddr))) continue; #undef __IN_IS_ADDR_LOOPBACK } else if (xip->inp_vflag & INP_IPV6) { if ((xip->inp_fport == 0 && !opt_l) || (xip->inp_fport != 0 && !opt_c)) continue; if (opt_L && (IN6_IS_ADDR_LOOPBACK(&xip->in6p_faddr) || IN6_IS_ADDR_LOOPBACK(&xip->in6p_laddr))) continue; } else { if (opt_v) warnx("invalid vflag 0x%x", xip->inp_vflag); continue; } if ((sock = calloc(1, sizeof(*sock))) == NULL) err(1, "malloc()"); if ((laddr = calloc(1, sizeof *laddr)) == NULL) err(1, "malloc()"); if ((faddr = calloc(1, sizeof *faddr)) == NULL) err(1, "malloc()"); sock->socket = so->xso_so; sock->proto = proto; sock->inp_gencnt = xip->inp_gencnt; if (xip->inp_vflag & INP_IPV4) { sock->family = AF_INET; sockaddr(&laddr->address, sock->family, &xip->inp_laddr, xip->inp_lport); sockaddr(&faddr->address, sock->family, &xip->inp_faddr, xip->inp_fport); } else if (xip->inp_vflag & INP_IPV6) { sock->family = AF_INET6; sockaddr(&laddr->address, sock->family, &xip->in6p_laddr, xip->inp_lport); sockaddr(&faddr->address, sock->family, &xip->in6p_faddr, xip->inp_fport); } if (proto == IPPROTO_TCP) faddr->encaps_port = xtp->xt_encaps_port; laddr->next = NULL; faddr->next = NULL; sock->laddr = laddr; sock->faddr = faddr; sock->vflag = xip->inp_vflag; if (proto == IPPROTO_TCP) { sock->state = xtp->t_state; memcpy(sock->stack, xtp->xt_stack, TCP_FUNCTION_NAME_LEN_MAX); memcpy(sock->cc, xtp->xt_cc, TCP_CA_NAME_MAX); } sock->protoname = protoname; if (sock->socket != 0) RB_INSERT(socks_t, &socks, sock); else SLIST_INSERT_HEAD(&nosocks, sock, socket_list); } out: free(buf); } static void gather_unix(int proto) { struct xunpgen *xug, *exug; struct xunpcb *xup; struct sock *sock; struct addr *laddr, *faddr; const char *varname, *protoname; size_t len, bufsize; void *buf; int retry; switch (proto) { case SOCK_STREAM: varname = "net.local.stream.pcblist"; protoname = "stream"; break; case SOCK_DGRAM: varname = "net.local.dgram.pcblist"; protoname = "dgram"; break; case SOCK_SEQPACKET: varname = "net.local.seqpacket.pcblist"; protoname = "seqpac"; break; default: abort(); } buf = NULL; bufsize = 8192; retry = 5; do { for (;;) { if ((buf = realloc(buf, bufsize)) == NULL) err(1, "realloc()"); len = bufsize; if (cap_sysctlbyname(capsysctl, varname, buf, &len, NULL, 0) == 0) break; if (errno != ENOMEM || len != bufsize) err(1, "cap_sysctlbyname()"); bufsize *= 2; } xug = (struct xunpgen *)buf; exug = (struct xunpgen *)(void *) ((char *)buf + len - sizeof(*exug)); if (!check_ksize(xug->xug_len, struct xunpgen) || !check_ksize(exug->xug_len, struct xunpgen)) goto out; } while (xug->xug_gen != exug->xug_gen && retry--); if (xug->xug_gen != exug->xug_gen && opt_v) warnx("warning: data may be inconsistent"); for (;;) { xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len); if (xug >= exug) break; xup = (struct xunpcb *)xug; if (!check_ksize(xup->xu_len, struct xunpcb)) goto out; if ((xup->unp_conn == 0 && !opt_l) || (xup->unp_conn != 0 && !opt_c)) continue; if ((sock = calloc(1, sizeof(*sock))) == NULL) err(1, "malloc()"); if ((laddr = calloc(1, sizeof *laddr)) == NULL) err(1, "malloc()"); if ((faddr = calloc(1, sizeof *faddr)) == NULL) err(1, "malloc()"); sock->socket = xup->xu_socket.xso_so; sock->pcb = xup->xu_unpp; sock->proto = proto; sock->family = AF_UNIX; sock->protoname = protoname; if (xup->xu_addr.sun_family == AF_UNIX) laddr->address = *(struct sockaddr_storage *)(void *)&xup->xu_addr; faddr->conn = xup->unp_conn; faddr->firstref = xup->xu_firstref; faddr->nextref = xup->xu_nextref; laddr->next = NULL; faddr->next = NULL; sock->laddr = laddr; sock->faddr = faddr; RB_INSERT(socks_t, &socks, sock); RB_INSERT(pcbs_t, &pcbs, sock); } out: free(buf); } static void getfiles(void) { struct xfile *xfiles; size_t len, olen; olen = len = sizeof(*xfiles); if ((xfiles = malloc(len)) == NULL) err(1, "malloc()"); while (cap_sysctlbyname(capsysctl, "kern.file", xfiles, &len, 0, 0) == -1) { if (errno != ENOMEM || len != olen) err(1, "cap_sysctlbyname()"); olen = len *= 2; if ((xfiles = realloc(xfiles, len)) == NULL) err(1, "realloc()"); } if (len > 0) enforce_ksize(xfiles->xf_size, struct xfile); nfiles = len / sizeof(*xfiles); if ((files = malloc(nfiles * sizeof(struct file))) == NULL) err(1, "malloc()"); for (int i = 0; i < nfiles; i++) { files[i].xf_data = xfiles[i].xf_data; files[i].xf_pid = xfiles[i].xf_pid; files[i].xf_uid = xfiles[i].xf_uid; files[i].xf_fd = xfiles[i].xf_fd; RB_INSERT(files_t, &ftree, &files[i]); } free(xfiles); } static int printaddr(struct sockaddr_storage *ss) { struct sockaddr_un *sun; char addrstr[NI_MAXHOST] = { '\0', '\0' }; int error, off, port = 0; switch (ss->ss_family) { case AF_INET: if (sstosin(ss)->sin_addr.s_addr == INADDR_ANY) addrstr[0] = '*'; port = ntohs(sstosin(ss)->sin_port); break; case AF_INET6: if (IN6_IS_ADDR_UNSPECIFIED(&sstosin6(ss)->sin6_addr)) addrstr[0] = '*'; port = ntohs(sstosin6(ss)->sin6_port); break; case AF_UNIX: sun = sstosun(ss); off = (int)((char *)&sun->sun_path - (char *)sun); return (xprintf("%.*s", sun->sun_len - off, sun->sun_path)); } if (addrstr[0] == '\0') { error = cap_getnameinfo(capnet, sstosa(ss), ss->ss_len, addrstr, sizeof(addrstr), NULL, 0, NI_NUMERICHOST); if (error) errx(1, "cap_getnameinfo()"); } if (port == 0) return xprintf("%s:*", addrstr); else return xprintf("%s:%d", addrstr, port); } static const char * getprocname(pid_t pid) { static struct kinfo_proc proc; size_t len; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = (int)pid; len = sizeof(proc); if (cap_sysctl(capsysctl, mib, nitems(mib), &proc, &len, NULL, 0) == -1) { /* Do not warn if the process exits before we get its name. */ if (errno != ESRCH) warn("cap_sysctl()"); return ("??"); } return (proc.ki_comm); } static int getprocjid(pid_t pid) { static struct kinfo_proc proc; size_t len; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = (int)pid; len = sizeof(proc); if (cap_sysctl(capsysctl, mib, nitems(mib), &proc, &len, NULL, 0) == -1) { /* Do not warn if the process exits before we get its jid. */ if (errno != ESRCH) warn("cap_sysctl()"); return (-1); } return (proc.ki_jid); } static int check_ports(struct sock *s) { int port; struct addr *addr; if (ports == NULL) return (1); if ((s->family != AF_INET) && (s->family != AF_INET6)) return (1); for (addr = s->laddr; addr != NULL; addr = addr->next) { if (s->family == AF_INET) port = ntohs(sstosin(&addr->address)->sin_port); else port = ntohs(sstosin6(&addr->address)->sin6_port); if (CHK_PORT(port)) return (1); } for (addr = s->faddr; addr != NULL; addr = addr->next) { if (s->family == AF_INET) port = ntohs(sstosin(&addr->address)->sin_port); else port = ntohs(sstosin6(&addr->address)->sin6_port); if (CHK_PORT(port)) return (1); } return (0); } static const char * sctp_conn_state(int state) { switch (state) { case SCTP_CLOSED: return "CLOSED"; break; case SCTP_BOUND: return "BOUND"; break; case SCTP_LISTEN: return "LISTEN"; break; case SCTP_COOKIE_WAIT: return "COOKIE_WAIT"; break; case SCTP_COOKIE_ECHOED: return "COOKIE_ECHOED"; break; case SCTP_ESTABLISHED: return "ESTABLISHED"; break; case SCTP_SHUTDOWN_SENT: return "SHUTDOWN_SENT"; break; case SCTP_SHUTDOWN_RECEIVED: return "SHUTDOWN_RECEIVED"; break; case SCTP_SHUTDOWN_ACK_SENT: return "SHUTDOWN_ACK_SENT"; break; case SCTP_SHUTDOWN_PENDING: return "SHUTDOWN_PENDING"; break; default: return "UNKNOWN"; break; } } static const char * sctp_path_state(int state) { switch (state) { case SCTP_UNCONFIRMED: return "UNCONFIRMED"; break; case SCTP_ACTIVE: return "ACTIVE"; break; case SCTP_INACTIVE: return "INACTIVE"; break; default: return "UNKNOWN"; break; } } static void displaysock(struct sock *s, int pos) { int first, offset; struct addr *laddr, *faddr; while (pos < 30) pos += xprintf(" "); pos += xprintf("%s", s->protoname); if (s->vflag & INP_IPV4) pos += xprintf("4"); if (s->vflag & INP_IPV6) pos += xprintf("6"); if (s->vflag & (INP_IPV4 | INP_IPV6)) pos += xprintf(" "); laddr = s->laddr; faddr = s->faddr; first = 1; while (laddr != NULL || faddr != NULL) { offset = 37; while (pos < offset) pos += xprintf(" "); switch (s->family) { case AF_INET: case AF_INET6: if (laddr != NULL) { pos += printaddr(&laddr->address); if (s->family == AF_INET6 && pos >= 58) pos += xprintf(" "); } offset += opt_w ? 46 : 22; while (pos < offset) pos += xprintf(" "); if (faddr != NULL) pos += printaddr(&faddr->address); offset += opt_w ? 46 : 22; break; case AF_UNIX: if ((laddr == NULL) || (faddr == NULL)) errx(1, "laddr = %p or faddr = %p is NULL", (void *)laddr, (void *)faddr); if (laddr->address.ss_len == 0 && faddr->conn == 0) { pos += xprintf("(not connected)"); offset += opt_w ? 92 : 44; break; } /* Local bind(2) address, if any. */ if (laddr->address.ss_len > 0) pos += printaddr(&laddr->address); /* Remote peer we connect(2) to, if any. */ if (faddr->conn != 0) { struct sock *p; pos += xprintf("%s-> ", laddr->address.ss_len > 0 ? " " : ""); p = RB_FIND(pcbs_t, &pcbs, &(struct sock){ .pcb = faddr->conn }); if (__predict_false(p == NULL)) { /* XXGL: can this happen at all? */ pos += xprintf("??"); } else if (p->laddr->address.ss_len == 0) { struct file *f; f = RB_FIND(files_t, &ftree, &(struct file){ .xf_data = p->socket }); pos += xprintf("[%lu %d]", (u_long)f->xf_pid, f->xf_fd); } else pos += printaddr(&p->laddr->address); } /* Remote peer(s) connect(2)ed to us, if any. */ if (faddr->firstref != 0) { struct sock *p; struct file *f; kvaddr_t ref = faddr->firstref; bool fref = true; pos += xprintf(" <- "); while ((p = RB_FIND(pcbs_t, &pcbs, &(struct sock){ .pcb = ref })) != 0) { f = RB_FIND(files_t, &ftree, &(struct file){ .xf_data = p->socket }); pos += xprintf("%s[%lu %d]", fref ? "" : ",", (u_long)f->xf_pid, f->xf_fd); ref = p->faddr->nextref; fref = false; } } offset += opt_w ? 92 : 44; break; default: abort(); } if (opt_i) { if (s->proto == IPPROTO_TCP || s->proto == IPPROTO_UDP) { while (pos < offset) pos += xprintf(" "); pos += xprintf("%" PRIu64, s->inp_gencnt); } offset += 9; } if (opt_U) { if (faddr != NULL && ((s->proto == IPPROTO_SCTP && s->state != SCTP_CLOSED && s->state != SCTP_BOUND && s->state != SCTP_LISTEN) || (s->proto == IPPROTO_TCP && s->state != TCPS_CLOSED && s->state != TCPS_LISTEN))) { while (pos < offset) pos += xprintf(" "); pos += xprintf("%u", ntohs(faddr->encaps_port)); } offset += 7; } if (opt_s) { if (faddr != NULL && s->proto == IPPROTO_SCTP && s->state != SCTP_CLOSED && s->state != SCTP_BOUND && s->state != SCTP_LISTEN) { while (pos < offset) pos += xprintf(" "); pos += xprintf("%s", sctp_path_state(faddr->state)); } offset += 13; } if (first) { if (opt_s) { if (s->proto == IPPROTO_SCTP || s->proto == IPPROTO_TCP) { while (pos < offset) pos += xprintf(" "); switch (s->proto) { case IPPROTO_SCTP: pos += xprintf("%s", sctp_conn_state(s->state)); break; case IPPROTO_TCP: if (s->state >= 0 && s->state < TCP_NSTATES) pos += xprintf("%s", tcpstates[s->state]); else pos += xprintf("?"); break; } } offset += 13; } if (opt_S) { if (s->proto == IPPROTO_TCP) { while (pos < offset) pos += xprintf(" "); pos += xprintf("%.*s", TCP_FUNCTION_NAME_LEN_MAX, s->stack); } offset += TCP_FUNCTION_NAME_LEN_MAX + 1; } if (opt_C) { if (s->proto == IPPROTO_TCP) { while (pos < offset) pos += xprintf(" "); xprintf("%.*s", TCP_CA_NAME_MAX, s->cc); } offset += TCP_CA_NAME_MAX + 1; } } if (laddr != NULL) laddr = laddr->next; if (faddr != NULL) faddr = faddr->next; if ((laddr != NULL) || (faddr != NULL)) { xprintf("\n"); pos = 0; } first = 0; } xprintf("\n"); } static void display(void) { struct passwd *pwd; struct file *xf; struct sock *s; int n, pos; if (opt_q != 1) { printf("%-8s %-10s %-5s %-3s %-6s %-*s %-*s", "USER", "COMMAND", "PID", "FD", "PROTO", opt_w ? 45 : 21, "LOCAL ADDRESS", opt_w ? 45 : 21, "FOREIGN ADDRESS"); if (opt_i) printf(" %-8s", "ID"); if (opt_U) printf(" %-6s", "ENCAPS"); if (opt_s) { printf(" %-12s", "PATH STATE"); printf(" %-12s", "CONN STATE"); } if (opt_S) printf(" %-*.*s", TCP_FUNCTION_NAME_LEN_MAX, TCP_FUNCTION_NAME_LEN_MAX, "STACK"); if (opt_C) printf(" %-.*s", TCP_CA_NAME_MAX, "CC"); printf("\n"); } cap_setpassent(cappwd, 1); for (xf = files, n = 0; n < nfiles; ++n, ++xf) { if (xf->xf_data == 0) continue; if (opt_j >= 0 && opt_j != getprocjid(xf->xf_pid)) continue; s = RB_FIND(socks_t, &socks, &(struct sock){ .socket = xf->xf_data}); if (s != NULL && check_ports(s)) { s->shown = 1; pos = 0; if (opt_n || (pwd = cap_getpwuid(cappwd, xf->xf_uid)) == NULL) pos += xprintf("%lu ", (u_long)xf->xf_uid); else pos += xprintf("%s ", pwd->pw_name); while (pos < 9) pos += xprintf(" "); pos += xprintf("%.10s", getprocname(xf->xf_pid)); while (pos < 20) pos += xprintf(" "); pos += xprintf("%5lu ", (u_long)xf->xf_pid); while (pos < 26) pos += xprintf(" "); pos += xprintf("%-3d ", xf->xf_fd); displaysock(s, pos); } } if (opt_j >= 0) return; SLIST_FOREACH(s, &nosocks, socket_list) { if (!check_ports(s)) continue; pos = xprintf("%-8s %-10s %-5s %-2s ", "?", "?", "?", "?"); displaysock(s, pos); } RB_FOREACH(s, socks_t, &socks) { if (s->shown) continue; if (!check_ports(s)) continue; pos = xprintf("%-8s %-10s %-5s %-2s ", "?", "?", "?", "?"); displaysock(s, pos); } } static int set_default_protos(void) { struct protoent *prot; const char *pname; size_t pindex; init_protos(default_numprotos); for (pindex = 0; pindex < default_numprotos; pindex++) { pname = default_protos[pindex]; prot = cap_getprotobyname(capnetdb, pname); if (prot == NULL) err(1, "cap_getprotobyname: %s", pname); protos[pindex] = prot->p_proto; } numprotos = pindex; return (pindex); } /* * Return the vnet property of the jail, or -1 on error. */ static int jail_getvnet(int jid) { struct iovec jiov[6]; int vnet; size_t len = sizeof(vnet); if (sysctlbyname("kern.features.vimage", &vnet, &len, NULL, 0) != 0) return (0); vnet = -1; jiov[0].iov_base = __DECONST(char *, "jid"); jiov[0].iov_len = sizeof("jid"); jiov[1].iov_base = &jid; jiov[1].iov_len = sizeof(jid); jiov[2].iov_base = __DECONST(char *, "vnet"); jiov[2].iov_len = sizeof("vnet"); jiov[3].iov_base = &vnet; jiov[3].iov_len = sizeof(vnet); jiov[4].iov_base = __DECONST(char *, "errmsg"); jiov[4].iov_len = sizeof("errmsg"); jiov[5].iov_base = jail_errmsg; jiov[5].iov_len = JAIL_ERRMSGLEN; jail_errmsg[0] = '\0'; if (jail_get(jiov, nitems(jiov), 0) < 0) { if (!jail_errmsg[0]) snprintf(jail_errmsg, JAIL_ERRMSGLEN, "jail_get: %s", strerror(errno)); return (-1); } return (vnet); } static void usage(void) { fprintf(stderr, "usage: sockstat [-46CciLlnqSsUuvw] [-j jid] [-p ports] [-P protocols]\n"); exit(1); } int main(int argc, char *argv[]) { cap_channel_t *capcas; cap_net_limit_t *limit; const char *pwdcmds[] = { "setpassent", "getpwuid" }; const char *pwdfields[] = { "pw_name" }; int protos_defined = -1; int o, i; opt_j = -1; while ((o = getopt(argc, argv, "46Ccij:Llnp:P:qSsUuvw")) != -1) switch (o) { case '4': opt_4 = 1; break; case '6': opt_6 = 1; break; case 'C': opt_C = 1; break; case 'c': opt_c = 1; break; case 'i': opt_i = 1; break; case 'j': opt_j = jail_getid(optarg); if (opt_j < 0) errx(1, "jail_getid: %s", jail_errmsg); break; case 'L': opt_L = 1; break; case 'l': opt_l = 1; break; case 'n': opt_n = 1; break; case 'p': parse_ports(optarg); break; case 'P': protos_defined = parse_protos(optarg); break; case 'q': opt_q = 1; break; case 'S': opt_S = 1; break; case 's': opt_s = 1; break; case 'U': opt_U = 1; break; case 'u': opt_u = 1; break; case 'v': ++opt_v; break; case 'w': opt_w = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc > 0) usage(); if (opt_j > 0) { switch (jail_getvnet(opt_j)) { case -1: errx(2, "jail_getvnet: %s", jail_errmsg); case JAIL_SYS_NEW: if (jail_attach(opt_j) < 0) err(3, "jail_attach()"); /* Set back to -1 for normal output in vnet jail. */ opt_j = -1; break; default: break; } } capcas = cap_init(); if (capcas == NULL) err(1, "Unable to contact Casper"); if (caph_enter_casper() < 0) err(1, "Unable to enter capability mode"); capnet = cap_service_open(capcas, "system.net"); if (capnet == NULL) err(1, "Unable to open system.net service"); capnetdb = cap_service_open(capcas, "system.netdb"); if (capnetdb == NULL) err(1, "Unable to open system.netdb service"); capsysctl = cap_service_open(capcas, "system.sysctl"); if (capsysctl == NULL) err(1, "Unable to open system.sysctl service"); cappwd = cap_service_open(capcas, "system.pwd"); if (cappwd == NULL) err(1, "Unable to open system.pwd service"); cap_close(capcas); limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME); if (limit == NULL) err(1, "Unable to init cap_net limits"); if (cap_net_limit(limit) < 0) err(1, "Unable to apply limits"); if (cap_pwd_limit_cmds(cappwd, pwdcmds, nitems(pwdcmds)) < 0) err(1, "Unable to apply pwd commands limits"); if (cap_pwd_limit_fields(cappwd, pwdfields, nitems(pwdfields)) < 0) err(1, "Unable to apply pwd commands limits"); if ((!opt_4 && !opt_6) && protos_defined != -1) opt_4 = opt_6 = 1; if (!opt_4 && !opt_6 && !opt_u) opt_4 = opt_6 = opt_u = 1; if ((opt_4 || opt_6) && protos_defined == -1) protos_defined = set_default_protos(); if (!opt_c && !opt_l) opt_c = opt_l = 1; if (opt_4 || opt_6) { for (i = 0; i < protos_defined; i++) if (protos[i] == IPPROTO_SCTP) gather_sctp(); else gather_inet(protos[i]); } if (opt_u || (protos_defined == -1 && !opt_4 && !opt_6)) { gather_unix(SOCK_STREAM); gather_unix(SOCK_DGRAM); gather_unix(SOCK_SEQPACKET); } getfiles(); display(); exit(0); } diff --git a/usr.bin/soelim/soelim.c b/usr.bin/soelim/soelim.c index 8d012a20254b..59328ba04874 100644 --- a/usr.bin/soelim/soelim.c +++ b/usr.bin/soelim/soelim.c @@ -1,175 +1,174 @@ /*- * Copyright (c) 2014 Baptiste Daroussin * 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 * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #define C_OPTION 0x1 static StringList *includes; static void usage(void) { fprintf(stderr, "usage: soelim [-Crtv] [-I dir] [files]\n"); exit(EXIT_FAILURE); } static FILE * soelim_fopen(const char *name) { FILE *f; char path[PATH_MAX]; size_t i; if (strcmp(name, "-") == 0) return (stdin); if ((f = fopen(name, "r")) != NULL) return (f); if (*name == '/') { warn("can't open '%s'", name); return (NULL); } for (i = 0; i < includes->sl_cur; i++) { snprintf(path, sizeof(path), "%s/%s", includes->sl_str[i], name); if ((f = fopen(path, "r")) != NULL) return (f); } warn("can't open '%s'", name); return (f); } static int soelim_file(FILE *f, int flag) { char *line = NULL; char *walk, *cp; size_t linecap = 0; ssize_t linelen; if (f == NULL) return (1); while ((linelen = getline(&line, &linecap, f)) > 0) { if (strncmp(line, ".so", 3) != 0) { printf("%s", line); continue; } walk = line + 3; if (!isspace(*walk) && ((flag & C_OPTION) == 0)) { printf("%s", line); continue; } while (isspace(*walk)) walk++; cp = walk; while (*cp != '\0' && !isspace(*cp)) cp++; *cp = 0; if (cp < line + linelen) cp++; if (*walk == '\0') { printf("%s", line); continue; } if (soelim_file(soelim_fopen(walk), flag) == 1) { free(line); return (1); } if (*cp != '\0') printf("%s", cp); } free(line); fclose(f); return (0); } int main(int argc, char **argv) { int ch, i; int ret = 0; int flags = 0; includes = sl_init(); if (includes == NULL) err(EXIT_FAILURE, "sl_init()"); while ((ch = getopt(argc, argv, "CrtvI:")) != -1) { switch (ch) { case 'C': flags |= C_OPTION; break; case 'r': case 'v': case 't': /* stub compatibility with groff's soelim */ break; case 'I': sl_add(includes, optarg); break; default: sl_free(includes, 0); usage(); } } argc -= optind; argv += optind; if (argc == 0) ret = soelim_file(stdin, flags); for (i = 0; i < argc; i++) ret = soelim_file(soelim_fopen(argv[i]), flags); sl_free(includes, 0); return (ret); } diff --git a/usr.bin/sort/coll.c b/usr.bin/sort/coll.c index a8bdc4c3e8ae..ff7a9a552dc0 100644 --- a/usr.bin/sort/coll.c +++ b/usr.bin/sort/coll.c @@ -1,1319 +1,1318 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 2009 Gabor Kovesdan * Copyright (C) 2012 Oleg Moskalenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include "coll.h" #include "vsort.h" struct key_specs *keys; size_t keys_num = 0; wint_t symbol_decimal_point = L'.'; /* there is no default thousands separator in collate rules: */ wint_t symbol_thousands_sep = 0; wint_t symbol_negative_sign = L'-'; wint_t symbol_positive_sign = L'+'; static int wstrcoll(struct key_value *kv1, struct key_value *kv2, size_t offset); static int gnumcoll(struct key_value*, struct key_value *, size_t offset); static int monthcoll(struct key_value*, struct key_value *, size_t offset); static int numcoll(struct key_value*, struct key_value *, size_t offset); static int hnumcoll(struct key_value*, struct key_value *, size_t offset); static int randomcoll(struct key_value*, struct key_value *, size_t offset); static int versioncoll(struct key_value*, struct key_value *, size_t offset); /* * Allocate keys array */ struct keys_array * keys_array_alloc(void) { struct keys_array *ka; size_t sz; sz = keys_array_size(); ka = sort_calloc(1, sz); return (ka); } /* * Calculate whether we need key hint space */ static size_t key_hint_size(void) { return (need_hint ? sizeof(struct key_hint) : 0); } /* * Calculate keys array size */ size_t keys_array_size(void) { return (keys_num * (sizeof(struct key_value) + key_hint_size())); } /* * Clean data of keys array */ void clean_keys_array(const struct bwstring *s, struct keys_array *ka) { if (ka) { for (size_t i = 0; i < keys_num; ++i) { const struct key_value *kv; kv = get_key_from_keys_array(ka, i); if (kv->k && kv->k != s) bwsfree(kv->k); } memset(ka, 0, keys_array_size()); } } /* * Get pointer to a key value in the keys set */ struct key_value * get_key_from_keys_array(struct keys_array *ka, size_t ind) { return ((struct key_value *)((caddr_t)ka->key + ind * (sizeof(struct key_value) + key_hint_size()))); } /* * Set value of a key in the keys set */ void set_key_on_keys_array(struct keys_array *ka, struct bwstring *s, size_t ind) { if (ka && keys_num > ind) { struct key_value *kv; kv = get_key_from_keys_array(ka, ind); if (kv->k && kv->k != s) bwsfree(kv->k); kv->k = s; } } /* * Initialize a sort list item */ struct sort_list_item * sort_list_item_alloc(void) { struct sort_list_item *si; size_t sz; sz = sizeof(struct sort_list_item) + keys_array_size(); si = sort_calloc(1, sz); return (si); } size_t sort_list_item_size(struct sort_list_item *si) { size_t ret = 0; if (si) { ret = sizeof(struct sort_list_item) + keys_array_size(); if (si->str) ret += bws_memsize(si->str); for (size_t i = 0; i < keys_num; ++i) { const struct key_value *kv; kv = get_key_from_keys_array(&si->ka, i); if (kv->k != si->str) ret += bws_memsize(kv->k); } } return (ret); } /* * Calculate key for a sort list item */ static void sort_list_item_make_key(struct sort_list_item *si) { preproc(si->str, &(si->ka)); } /* * Set value of a sort list item. * Return combined string and keys memory size. */ void sort_list_item_set(struct sort_list_item *si, struct bwstring *str) { if (si) { clean_keys_array(si->str, &(si->ka)); if (si->str) { if (si->str == str) { /* we are trying to reset the same string */ return; } else { bwsfree(si->str); si->str = NULL; } } si->str = str; sort_list_item_make_key(si); } } /* * De-allocate a sort list item object memory */ void sort_list_item_clean(struct sort_list_item *si) { if (si) { clean_keys_array(si->str, &(si->ka)); if (si->str) { bwsfree(si->str); si->str = NULL; } } } /* * Skip columns according to specs */ static size_t skip_cols_to_start(const struct bwstring *s, size_t cols, size_t start, bool skip_blanks, bool *empty_key) { if (cols < 1) return (BWSLEN(s) + 1); if (skip_blanks) while (start < BWSLEN(s) && iswblank(BWS_GET(s,start))) ++start; while (start < BWSLEN(s) && cols > 1) { --cols; ++start; } if (start >= BWSLEN(s)) *empty_key = true; return (start); } /* * Skip fields according to specs */ static size_t skip_fields_to_start(const struct bwstring *s, size_t fields, bool *empty_field) { if (fields < 2) { if (BWSLEN(s) == 0) *empty_field = true; return (0); } else if (!(sort_opts_vals.tflag)) { size_t cpos = 0; bool pb = true; while (cpos < BWSLEN(s)) { bool isblank; isblank = iswblank(BWS_GET(s, cpos)); if (isblank && !pb) { --fields; if (fields <= 1) return (cpos); } pb = isblank; ++cpos; } if (fields > 1) *empty_field = true; return (cpos); } else { size_t cpos = 0; while (cpos < BWSLEN(s)) { if (BWS_GET(s,cpos) == (wchar_t)sort_opts_vals.field_sep) { --fields; if (fields <= 1) return (cpos + 1); } ++cpos; } if (fields > 1) *empty_field = true; return (cpos); } } /* * Find fields start */ static void find_field_start(const struct bwstring *s, struct key_specs *ks, size_t *field_start, size_t *key_start, bool *empty_field, bool *empty_key) { *field_start = skip_fields_to_start(s, ks->f1, empty_field); if (!*empty_field) *key_start = skip_cols_to_start(s, ks->c1, *field_start, ks->pos1b, empty_key); else *empty_key = true; } /* * Find end key position */ static size_t find_field_end(const struct bwstring *s, struct key_specs *ks) { size_t f2, next_field_start, pos_end; bool empty_field, empty_key; empty_field = false; empty_key = false; f2 = ks->f2; if (f2 == 0) return (BWSLEN(s) + 1); else { if (ks->c2 == 0) { next_field_start = skip_fields_to_start(s, f2 + 1, &empty_field); if ((next_field_start > 0) && sort_opts_vals.tflag && ((wchar_t)sort_opts_vals.field_sep == BWS_GET(s, next_field_start - 1))) --next_field_start; } else next_field_start = skip_fields_to_start(s, f2, &empty_field); } if (empty_field || (next_field_start >= BWSLEN(s))) return (BWSLEN(s) + 1); if (ks->c2) { pos_end = skip_cols_to_start(s, ks->c2, next_field_start, ks->pos2b, &empty_key); if (pos_end < BWSLEN(s)) ++pos_end; } else pos_end = next_field_start; return (pos_end); } /* * Cut a field according to the key specs */ static struct bwstring * cut_field(const struct bwstring *s, struct key_specs *ks) { struct bwstring *ret = NULL; if (s && ks) { size_t field_start, key_end, key_start, sz; bool empty_field, empty_key; field_start = 0; key_start = 0; empty_field = false; empty_key = false; find_field_start(s, ks, &field_start, &key_start, &empty_field, &empty_key); if (empty_key) sz = 0; else { key_end = find_field_end(s, ks); sz = (key_end < key_start) ? 0 : (key_end - key_start); } ret = bwsalloc(sz); if (sz) bwsnocpy(ret, s, key_start, sz); } else ret = bwsalloc(0); return (ret); } /* * Preprocesses a line applying the necessary transformations * specified by command line options and returns the preprocessed * string, which can be used to compare. */ int preproc(struct bwstring *s, struct keys_array *ka) { if (sort_opts_vals.kflag) for (size_t i = 0; i < keys_num; i++) { struct bwstring *key; struct key_specs *kspecs; struct sort_mods *sm; kspecs = &(keys[i]); key = cut_field(s, kspecs); sm = &(kspecs->sm); if (sm->dflag) key = dictionary_order(key); else if (sm->iflag) key = ignore_nonprinting(key); if (sm->fflag || sm->Mflag) key = ignore_case(key); set_key_on_keys_array(ka, key, i); } else { struct bwstring *ret = NULL; struct sort_mods *sm = default_sort_mods; if (sm->bflag) { if (ret == NULL) ret = bwsdup(s); ret = ignore_leading_blanks(ret); } if (sm->dflag) { if (ret == NULL) ret = bwsdup(s); ret = dictionary_order(ret); } else if (sm->iflag) { if (ret == NULL) ret = bwsdup(s); ret = ignore_nonprinting(ret); } if (sm->fflag || sm->Mflag) { if (ret == NULL) ret = bwsdup(s); ret = ignore_case(ret); } if (ret == NULL) set_key_on_keys_array(ka, s, 0); else set_key_on_keys_array(ka, ret, 0); } return 0; } cmpcoll_t get_sort_func(struct sort_mods *sm) { if (sm->nflag) return (numcoll); else if (sm->hflag) return (hnumcoll); else if (sm->gflag) return (gnumcoll); else if (sm->Mflag) return (monthcoll); else if (sm->Rflag) return (randomcoll); else if (sm->Vflag) return (versioncoll); else return (wstrcoll); } /* * Compares the given strings. Returns a positive number if * the first precedes the second, a negative number if the second is * the preceding one, and zero if they are equal. This function calls * the underlying collate functions, which done the actual comparison. */ int key_coll(struct keys_array *ps1, struct keys_array *ps2, size_t offset) { struct key_value *kv1, *kv2; struct sort_mods *sm; int res = 0; for (size_t i = 0; i < keys_num; ++i) { kv1 = get_key_from_keys_array(ps1, i); kv2 = get_key_from_keys_array(ps2, i); sm = &(keys[i].sm); if (sm->rflag) res = sm->func(kv2, kv1, offset); else res = sm->func(kv1, kv2, offset); if (res) break; /* offset applies to only the first key */ offset = 0; } return (res); } /* * Compare two strings. * Plain symbol-by-symbol comparison. */ int top_level_str_coll(const struct bwstring *s1, const struct bwstring *s2) { if (default_sort_mods->rflag) { const struct bwstring *tmp; tmp = s1; s1 = s2; s2 = tmp; } return (bwscoll(s1, s2, 0)); } /* * Compare a string and a sort list item, according to the sort specs. */ int str_list_coll(struct bwstring *str1, struct sort_list_item **ss2) { struct keys_array *ka1; int ret = 0; ka1 = keys_array_alloc(); preproc(str1, ka1); sort_list_item_make_key(*ss2); if (debug_sort) { bwsprintf(stdout, str1, "; s1=<", ">"); bwsprintf(stdout, (*ss2)->str, ", s2=<", ">"); } ret = key_coll(ka1, &((*ss2)->ka), 0); if (debug_sort) printf("; cmp1=%d", ret); clean_keys_array(str1, ka1); sort_free(ka1); if ((ret == 0) && !(sort_opts_vals.sflag) && sort_opts_vals.complex_sort) { ret = top_level_str_coll(str1, ((*ss2)->str)); if (debug_sort) printf("; cmp2=%d", ret); } if (debug_sort) printf("\n"); return (ret); } /* * Compare two sort list items, according to the sort specs. */ int list_coll_offset(struct sort_list_item **ss1, struct sort_list_item **ss2, size_t offset) { int ret; ret = key_coll(&((*ss1)->ka), &((*ss2)->ka), offset); if (debug_sort) { if (offset) printf("; offset=%d", (int) offset); bwsprintf(stdout, ((*ss1)->str), "; s1=<", ">"); bwsprintf(stdout, ((*ss2)->str), ", s2=<", ">"); printf("; cmp1=%d\n", ret); } if (ret) return (ret); if (!(sort_opts_vals.sflag) && sort_opts_vals.complex_sort) { ret = top_level_str_coll(((*ss1)->str), ((*ss2)->str)); if (debug_sort) printf("; cmp2=%d\n", ret); } return (ret); } /* * Compare two sort list items, according to the sort specs. */ int list_coll(struct sort_list_item **ss1, struct sort_list_item **ss2) { return (list_coll_offset(ss1, ss2, 0)); } #define LSCDEF(N) \ static int \ list_coll_##N(struct sort_list_item **ss1, struct sort_list_item **ss2) \ { \ \ return (list_coll_offset(ss1, ss2, N)); \ } LSCDEF(1) LSCDEF(2) LSCDEF(3) LSCDEF(4) LSCDEF(5) LSCDEF(6) LSCDEF(7) LSCDEF(8) LSCDEF(9) LSCDEF(10) LSCDEF(11) LSCDEF(12) LSCDEF(13) LSCDEF(14) LSCDEF(15) LSCDEF(16) LSCDEF(17) LSCDEF(18) LSCDEF(19) LSCDEF(20) listcoll_t get_list_call_func(size_t offset) { static const listcoll_t lsarray[] = { list_coll, list_coll_1, list_coll_2, list_coll_3, list_coll_4, list_coll_5, list_coll_6, list_coll_7, list_coll_8, list_coll_9, list_coll_10, list_coll_11, list_coll_12, list_coll_13, list_coll_14, list_coll_15, list_coll_16, list_coll_17, list_coll_18, list_coll_19, list_coll_20 }; if (offset <= 20) return (lsarray[offset]); return (list_coll); } /* * Compare two sort list items, only by their original string. */ int list_coll_by_str_only(struct sort_list_item **ss1, struct sort_list_item **ss2) { return (top_level_str_coll(((*ss1)->str), ((*ss2)->str))); } /* * Maximum size of a number in the string (before or after decimal point) */ #define MAX_NUM_SIZE (128) /* * Set suffix value */ static void setsuffix(wchar_t c, unsigned char *si) { switch (c){ case L'k': case L'K': *si = 1; break; case L'M': *si = 2; break; case L'G': *si = 3; break; case L'T': *si = 4; break; case L'P': *si = 5; break; case L'E': *si = 6; break; case L'Z': *si = 7; break; case L'Y': *si = 8; break; default: *si = 0; } } /* * Read string s and parse the string into a fixed-decimal-point number. * sign equals -1 if the number is negative (explicit plus is not allowed, * according to GNU sort's "info sort". * The number part before decimal point is in the smain, after the decimal * point is in sfrac, tail is the pointer to the remainder of the string. */ static int read_number(struct bwstring *s0, int *sign, wchar_t *smain, size_t *main_len, wchar_t *sfrac, size_t *frac_len, unsigned char *si) { bwstring_iterator s; s = bws_begin(s0); /* always end the fraction with zero, even if we have no fraction */ sfrac[0] = 0; while (iswblank(bws_get_iter_value(s))) s = bws_iterator_inc(s, 1); if (bws_get_iter_value(s) == (wchar_t)symbol_negative_sign) { *sign = -1; s = bws_iterator_inc(s, 1); } // This is '0', not '\0', do not change this while (iswdigit(bws_get_iter_value(s)) && (bws_get_iter_value(s) == L'0')) s = bws_iterator_inc(s, 1); while (bws_get_iter_value(s) && *main_len < MAX_NUM_SIZE) { if (iswdigit(bws_get_iter_value(s))) { smain[*main_len] = bws_get_iter_value(s); s = bws_iterator_inc(s, 1); *main_len += 1; } else if (symbol_thousands_sep && (bws_get_iter_value(s) == (wchar_t)symbol_thousands_sep)) s = bws_iterator_inc(s, 1); else break; } smain[*main_len] = 0; if (bws_get_iter_value(s) == (wchar_t)symbol_decimal_point) { s = bws_iterator_inc(s, 1); while (iswdigit(bws_get_iter_value(s)) && *frac_len < MAX_NUM_SIZE) { sfrac[*frac_len] = bws_get_iter_value(s); s = bws_iterator_inc(s, 1); *frac_len += 1; } sfrac[*frac_len] = 0; while (*frac_len > 0 && sfrac[*frac_len - 1] == L'0') { --(*frac_len); sfrac[*frac_len] = L'\0'; } } setsuffix(bws_get_iter_value(s),si); if ((*main_len + *frac_len) == 0) *sign = 0; return (0); } /* * Implements string sort. */ static int wstrcoll(struct key_value *kv1, struct key_value *kv2, size_t offset) { if (debug_sort) { if (offset) printf("; offset=%d\n", (int) offset); bwsprintf(stdout, kv1->k, "; k1=<", ">"); printf("(%zu)", BWSLEN(kv1->k)); bwsprintf(stdout, kv2->k, ", k2=<", ">"); printf("(%zu)", BWSLEN(kv2->k)); } return (bwscoll(kv1->k, kv2->k, offset)); } /* * Compare two suffixes */ static inline int cmpsuffix(unsigned char si1, unsigned char si2) { return ((char)si1 - (char)si2); } /* * Implements numeric sort for -n and -h. */ static int numcoll_impl(struct key_value *kv1, struct key_value *kv2, size_t offset __unused, bool use_suffix) { struct bwstring *s1, *s2; wchar_t sfrac1[MAX_NUM_SIZE + 1], sfrac2[MAX_NUM_SIZE + 1]; wchar_t smain1[MAX_NUM_SIZE + 1], smain2[MAX_NUM_SIZE + 1]; int cmp_res, sign1, sign2; size_t frac1, frac2, main1, main2; unsigned char SI1, SI2; bool e1, e2, key1_read, key2_read; s1 = kv1->k; s2 = kv2->k; sign1 = sign2 = 0; main1 = main2 = 0; frac1 = frac2 = 0; key1_read = key2_read = false; if (debug_sort) { bwsprintf(stdout, s1, "; k1=<", ">"); bwsprintf(stdout, s2, ", k2=<", ">"); } if (s1 == s2) return (0); if (kv1->hint->status == HS_UNINITIALIZED) { /* read the number from the string */ read_number(s1, &sign1, smain1, &main1, sfrac1, &frac1, &SI1); key1_read = true; kv1->hint->v.nh.n1 = wcstoull(smain1, NULL, 10); if(main1 < 1 && frac1 < 1) kv1->hint->v.nh.empty=true; kv1->hint->v.nh.si = SI1; kv1->hint->status = (kv1->hint->v.nh.n1 != ULLONG_MAX) ? HS_INITIALIZED : HS_ERROR; kv1->hint->v.nh.neg = (sign1 < 0) ? true : false; } if (kv2->hint->status == HS_UNINITIALIZED) { /* read the number from the string */ read_number(s2, &sign2, smain2, &main2, sfrac2, &frac2,&SI2); key2_read = true; kv2->hint->v.nh.n1 = wcstoull(smain2, NULL, 10); if(main2 < 1 && frac2 < 1) kv2->hint->v.nh.empty=true; kv2->hint->v.nh.si = SI2; kv2->hint->status = (kv2->hint->v.nh.n1 != ULLONG_MAX) ? HS_INITIALIZED : HS_ERROR; kv2->hint->v.nh.neg = (sign2 < 0) ? true : false; } if (kv1->hint->status == HS_INITIALIZED && kv2->hint->status == HS_INITIALIZED) { unsigned long long n1, n2; bool neg1, neg2; e1 = kv1->hint->v.nh.empty; e2 = kv2->hint->v.nh.empty; if (e1 && e2) return (0); neg1 = kv1->hint->v.nh.neg; neg2 = kv2->hint->v.nh.neg; if (neg1 && !neg2) return (-1); if (neg2 && !neg1) return (+1); if (e1) return (neg2 ? +1 : -1); else if (e2) return (neg1 ? -1 : +1); if (use_suffix) { cmp_res = cmpsuffix(kv1->hint->v.nh.si, kv2->hint->v.nh.si); if (cmp_res) return (neg1 ? -cmp_res : cmp_res); } n1 = kv1->hint->v.nh.n1; n2 = kv2->hint->v.nh.n1; if (n1 < n2) return (neg1 ? +1 : -1); else if (n1 > n2) return (neg1 ? -1 : +1); } /* read the numbers from the strings */ if (!key1_read) read_number(s1, &sign1, smain1, &main1, sfrac1, &frac1, &SI1); if (!key2_read) read_number(s2, &sign2, smain2, &main2, sfrac2, &frac2, &SI2); e1 = ((main1 + frac1) == 0); e2 = ((main2 + frac2) == 0); if (e1 && e2) return (0); /* we know the result if the signs are different */ if (sign1 < 0 && sign2 >= 0) return (-1); if (sign1 >= 0 && sign2 < 0) return (+1); if (e1) return ((sign2 < 0) ? +1 : -1); else if (e2) return ((sign1 < 0) ? -1 : +1); if (use_suffix) { cmp_res = cmpsuffix(SI1, SI2); if (cmp_res) return ((sign1 < 0) ? -cmp_res : cmp_res); } /* if both numbers are empty assume that the strings are equal */ if (main1 < 1 && main2 < 1 && frac1 < 1 && frac2 < 1) return (0); /* * if the main part is of different size, we know the result * (because the leading zeros are removed) */ if (main1 < main2) cmp_res = -1; else if (main1 > main2) cmp_res = +1; /* if the sizes are equal then simple non-collate string compare gives the correct result */ else cmp_res = wcscmp(smain1, smain2); /* check fraction */ if (!cmp_res) cmp_res = wcscmp(sfrac1, sfrac2); if (!cmp_res) return (0); /* reverse result if the signs are negative */ if (sign1 < 0 && sign2 < 0) cmp_res = -cmp_res; return (cmp_res); } /* * Implements numeric sort (-n). */ static int numcoll(struct key_value *kv1, struct key_value *kv2, size_t offset) { return (numcoll_impl(kv1, kv2, offset, false)); } /* * Implements 'human' numeric sort (-h). */ static int hnumcoll(struct key_value *kv1, struct key_value *kv2, size_t offset) { return (numcoll_impl(kv1, kv2, offset, true)); } /* Use hint space to memoize md5 computations, at least. */ static void randomcoll_init_hint(struct key_value *kv, void *hash) { memcpy(kv->hint->v.Rh.cached, hash, sizeof(kv->hint->v.Rh.cached)); kv->hint->status = HS_INITIALIZED; } /* * Implements random sort (-R). */ static int randomcoll(struct key_value *kv1, struct key_value *kv2, size_t offset __unused) { struct bwstring *s1, *s2; MD5_CTX ctx1, ctx2; unsigned char hash1[MD5_DIGEST_LENGTH], hash2[MD5_DIGEST_LENGTH]; int cmp; s1 = kv1->k; s2 = kv2->k; if (debug_sort) { bwsprintf(stdout, s1, "; k1=<", ">"); bwsprintf(stdout, s2, ", k2=<", ">"); } if (s1 == s2) return (0); if (kv1->hint->status == HS_INITIALIZED && kv2->hint->status == HS_INITIALIZED) { cmp = memcmp(kv1->hint->v.Rh.cached, kv2->hint->v.Rh.cached, sizeof(kv1->hint->v.Rh.cached)); if (cmp != 0) return (cmp); } memcpy(&ctx1, &md5_ctx, sizeof(MD5_CTX)); memcpy(&ctx2, &md5_ctx, sizeof(MD5_CTX)); MD5Update(&ctx1, bwsrawdata(s1), bwsrawlen(s1)); MD5Update(&ctx2, bwsrawdata(s2), bwsrawlen(s2)); MD5Final(hash1, &ctx1); MD5Final(hash2, &ctx2); if (kv1->hint->status == HS_UNINITIALIZED) randomcoll_init_hint(kv1, hash1); if (kv2->hint->status == HS_UNINITIALIZED) randomcoll_init_hint(kv2, hash2); return (memcmp(hash1, hash2, sizeof(hash1))); } /* * Implements version sort (-V). */ static int versioncoll(struct key_value *kv1, struct key_value *kv2, size_t offset __unused) { struct bwstring *s1, *s2; s1 = kv1->k; s2 = kv2->k; if (debug_sort) { bwsprintf(stdout, s1, "; k1=<", ">"); bwsprintf(stdout, s2, ", k2=<", ">"); } if (s1 == s2) return (0); return (vcmp(s1, s2)); } /* * Check for minus infinity */ static inline bool huge_minus(double d, int err1) { if (err1 == ERANGE) if (d == -HUGE_VAL || d == -HUGE_VALF || d == -HUGE_VALL) return (+1); return (0); } /* * Check for plus infinity */ static inline bool huge_plus(double d, int err1) { if (err1 == ERANGE) if (d == HUGE_VAL || d == HUGE_VALF || d == HUGE_VALL) return (+1); return (0); } /* * Check whether a function is a NAN */ static bool is_nan(double d) { return ((d == NAN) || (isnan(d))); } /* * Compare two NANs */ static int cmp_nans(double d1, double d2) { if (d1 < d2) return (-1); if (d1 > d2) return (+1); return (0); } /* * Implements general numeric sort (-g). */ static int gnumcoll(struct key_value *kv1, struct key_value *kv2, size_t offset __unused) { double d1, d2; int err1, err2; bool empty1, empty2, key1_read, key2_read; d1 = d2 = 0; err1 = err2 = 0; key1_read = key2_read = false; if (debug_sort) { bwsprintf(stdout, kv1->k, "; k1=<", ">"); bwsprintf(stdout, kv2->k, "; k2=<", ">"); } if (kv1->hint->status == HS_UNINITIALIZED) { errno = 0; d1 = bwstod(kv1->k, &empty1); err1 = errno; if (empty1) kv1->hint->v.gh.notnum = true; else if (err1 == 0) { kv1->hint->v.gh.d = d1; kv1->hint->v.gh.nan = is_nan(d1); kv1->hint->status = HS_INITIALIZED; } else kv1->hint->status = HS_ERROR; key1_read = true; } if (kv2->hint->status == HS_UNINITIALIZED) { errno = 0; d2 = bwstod(kv2->k, &empty2); err2 = errno; if (empty2) kv2->hint->v.gh.notnum = true; else if (err2 == 0) { kv2->hint->v.gh.d = d2; kv2->hint->v.gh.nan = is_nan(d2); kv2->hint->status = HS_INITIALIZED; } else kv2->hint->status = HS_ERROR; key2_read = true; } if (kv1->hint->status == HS_INITIALIZED && kv2->hint->status == HS_INITIALIZED) { if (kv1->hint->v.gh.notnum) return ((kv2->hint->v.gh.notnum) ? 0 : -1); else if (kv2->hint->v.gh.notnum) return (+1); if (kv1->hint->v.gh.nan) return ((kv2->hint->v.gh.nan) ? cmp_nans(kv1->hint->v.gh.d, kv2->hint->v.gh.d) : -1); else if (kv2->hint->v.gh.nan) return (+1); d1 = kv1->hint->v.gh.d; d2 = kv2->hint->v.gh.d; if (d1 < d2) return (-1); else if (d1 > d2) return (+1); else return (0); } if (!key1_read) { errno = 0; d1 = bwstod(kv1->k, &empty1); err1 = errno; } if (!key2_read) { errno = 0; d2 = bwstod(kv2->k, &empty2); err2 = errno; } /* Non-value case: */ if (empty1) return (empty2 ? 0 : -1); else if (empty2) return (+1); /* NAN case */ if (is_nan(d1)) return (is_nan(d2) ? cmp_nans(d1, d2) : -1); else if (is_nan(d2)) return (+1); /* Infinities */ if (err1 == ERANGE || err2 == ERANGE) { /* Minus infinity case */ if (huge_minus(d1, err1)) { if (huge_minus(d2, err2)) { if (d1 < d2) return (-1); if (d1 > d2) return (+1); return (0); } else return (-1); } else if (huge_minus(d2, err2)) { if (huge_minus(d1, err1)) { if (d1 < d2) return (-1); if (d1 > d2) return (+1); return (0); } else return (+1); } /* Plus infinity case */ if (huge_plus(d1, err1)) { if (huge_plus(d2, err2)) { if (d1 < d2) return (-1); if (d1 > d2) return (+1); return (0); } else return (+1); } else if (huge_plus(d2, err2)) { if (huge_plus(d1, err1)) { if (d1 < d2) return (-1); if (d1 > d2) return (+1); return (0); } else return (-1); } } if (d1 < d2) return (-1); if (d1 > d2) return (+1); return (0); } /* * Implements month sort (-M). */ static int monthcoll(struct key_value *kv1, struct key_value *kv2, size_t offset __unused) { int val1, val2; bool key1_read, key2_read; val1 = val2 = 0; key1_read = key2_read = false; if (debug_sort) { bwsprintf(stdout, kv1->k, "; k1=<", ">"); bwsprintf(stdout, kv2->k, "; k2=<", ">"); } if (kv1->hint->status == HS_UNINITIALIZED) { kv1->hint->v.Mh.m = bws_month_score(kv1->k); key1_read = true; kv1->hint->status = HS_INITIALIZED; } if (kv2->hint->status == HS_UNINITIALIZED) { kv2->hint->v.Mh.m = bws_month_score(kv2->k); key2_read = true; kv2->hint->status = HS_INITIALIZED; } if (kv1->hint->status == HS_INITIALIZED) { val1 = kv1->hint->v.Mh.m; key1_read = true; } if (kv2->hint->status == HS_INITIALIZED) { val2 = kv2->hint->v.Mh.m; key2_read = true; } if (!key1_read) val1 = bws_month_score(kv1->k); if (!key2_read) val2 = bws_month_score(kv2->k); if (val1 == val2) { return (0); } if (val1 < val2) return (-1); return (+1); } diff --git a/usr.bin/sort/vsort.c b/usr.bin/sort/vsort.c index 6b04feff8f6b..45bcbb532ad5 100644 --- a/usr.bin/sort/vsort.c +++ b/usr.bin/sort/vsort.c @@ -1,265 +1,264 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 2012 Oleg Moskalenko * Copyright (C) 2012 Gabor Kovesdan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include "sort.h" #include "vsort.h" static inline bool isdigit_clocale(wchar_t c) { return (c >= L'0' && c <= L'9'); } static inline bool isalpha_clocale(wchar_t c) { return ((c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z')); } static inline bool isalnum_clocale(wchar_t c) { return ((c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z') || (c >= L'0' && c <= L'9')); } /* * Find string suffix of format: (\.[A-Za-z~][A-Za-z0-9~]*)*$ * Set length of string before suffix. */ static void find_suffix(bwstring_iterator si, bwstring_iterator se, size_t *len) { wchar_t c; size_t clen; bool expect_alpha, sfx; sfx = false; expect_alpha = false; *len = 0; clen = 0; while ((si < se) && (c = bws_get_iter_value(si))) { if (expect_alpha) { expect_alpha = false; if (!isalpha_clocale(c) && (c != L'~')) sfx = false; } else if (c == L'.') { expect_alpha = true; if (!sfx) { sfx = true; *len = clen; } } else if (!isalnum_clocale(c) && (c != L'~')) sfx = false; si = bws_iterator_inc(si, 1); ++clen; } /* This code must be here to make the implementation compatible * with WORDING of GNU sort documentation. * But the GNU sort implementation is not following its own * documentation. GNU sort allows empty file extensions * (just dot with nothing after); but the regular expression in * their documentation does not allow empty file extensions. * We chose to make our implementation compatible with GNU sort * implementation. If they will ever fix their bug, this code * must be uncommented. Or they may choose to fix the info page, * then the code stays commented. * if (expect_alpha) sfx = false; */ if (!sfx) *len = clen; } static inline int cmp_chars(wchar_t c1, wchar_t c2) { if (c1 == c2) return (0); if (c1 == L'~') return (-1); if (c2 == L'~') return (+1); if (isdigit_clocale(c1) || !c1) return ((isdigit_clocale(c2) || !c2) ? 0 : -1); if (isdigit_clocale(c2) || !c2) return (+1); if (isalpha_clocale(c1)) return ((isalpha_clocale(c2)) ? ((int) c1 - (int) c2) : -1); if (isalpha_clocale(c2)) return (+1); return ((int) c1 - (int) c2); } static int cmpversions(bwstring_iterator si1, bwstring_iterator se1, bwstring_iterator si2, bwstring_iterator se2) { int cmp, diff; while ((si1 < se1) || (si2 < se2)) { diff = 0; while (((si1 < se1) && !isdigit_clocale(bws_get_iter_value(si1))) || ((si2 < se2) && !isdigit_clocale(bws_get_iter_value(si2)))) { wchar_t c1, c2; c1 = (si1 < se1) ? bws_get_iter_value(si1) : 0; c2 = (si2 < se2) ? bws_get_iter_value(si2) : 0; cmp = cmp_chars(c1, c2); if (cmp) return (cmp); if (si1 < se1) si1 = bws_iterator_inc(si1, 1); if (si2 < se2) si2 = bws_iterator_inc(si2, 1); } while (bws_get_iter_value(si1) == L'0') si1 = bws_iterator_inc(si1, 1); while (bws_get_iter_value(si2) == L'0') si2 = bws_iterator_inc(si2, 1); while (isdigit_clocale(bws_get_iter_value(si1)) && isdigit_clocale(bws_get_iter_value(si2))) { if (!diff) diff = ((int)bws_get_iter_value(si1) - (int)bws_get_iter_value(si2)); si1 = bws_iterator_inc(si1, 1); si2 = bws_iterator_inc(si2, 1); } if (isdigit_clocale(bws_get_iter_value(si1))) return (1); if (isdigit_clocale(bws_get_iter_value(si2))) return (-1); if (diff) return (diff); } return (0); } /* * Compare two version strings */ int vcmp(struct bwstring *s1, struct bwstring *s2) { bwstring_iterator si1, si2; wchar_t c1, c2; size_t len1, len2, slen1, slen2; int cmp_bytes, cmp_res; if (s1 == s2) return (0); cmp_bytes = bwscmp(s1, s2, 0); if (cmp_bytes == 0) return (0); len1 = slen1 = BWSLEN(s1); len2 = slen2 = BWSLEN(s2); if (slen1 < 1) return (-1); if (slen2 < 1) return (+1); si1 = bws_begin(s1); si2 = bws_begin(s2); c1 = bws_get_iter_value(si1); c2 = bws_get_iter_value(si2); if (c1 == L'.' && (slen1 == 1)) return (-1); if (c2 == L'.' && (slen2 == 1)) return (+1); if (slen1 == 2 && c1 == L'.' && bws_get_iter_value(bws_iterator_inc(si1, 1)) == L'.') return (-1); if (slen2 == 2 && c2 == L'.' && bws_get_iter_value(bws_iterator_inc(si2, 1)) == L'.') return (+1); if (c1 == L'.' && c2 != L'.') return (-1); if (c1 != L'.' && c2 == L'.') return (+1); if (c1 == L'.' && c2 == L'.') { si1 = bws_iterator_inc(si1, 1); si2 = bws_iterator_inc(si2, 1); } find_suffix(si1, bws_end(s1), &len1); find_suffix(si2, bws_end(s2), &len2); if ((len1 == len2) && (bws_iterator_cmp(si1, si2, len1) == 0)) return (cmp_bytes); cmp_res = cmpversions(si1, bws_iterator_inc(si1, len1), si2, bws_iterator_inc(si2, len2)); if (cmp_res == 0) cmp_res = cmp_bytes; return (cmp_res); } diff --git a/usr.bin/systat/pigs.c b/usr.bin/systat/pigs.c index 7f4895a13db6..8a61505858cf 100644 --- a/usr.bin/systat/pigs.c +++ b/usr.bin/systat/pigs.c @@ -1,187 +1,184 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include /* * Pigs display from Bill Reeves at Lucasfilm */ #include #include #include #include #include #include #include #include #include #include "systat.h" #include "extern.h" int compar(const void *, const void *); static int nproc; static struct p_times { float pt_pctcpu; struct kinfo_proc *pt_kp; } *pt = NULL; static int fscale; static double lccpu; WINDOW * openpigs(void) { return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0)); } void closepigs(WINDOW *w) { if (w == NULL) return; wclear(w); wrefresh(w); delwin(w); } void showpigs(void) { int i, j, y, k; const char *uname, *pname; char pidname[30]; if (nproc == 0) return; qsort(pt, nproc, sizeof (struct p_times), compar); y = 1; i = nproc; if (i > getmaxy(wnd)-2) i = getmaxy(wnd)-2; for (k = 0; i > 0 && pt[k].pt_pctcpu > 0.01; i--, y++, k++) { uname = user_from_uid(pt[k].pt_kp->ki_uid, 0); pname = pt[k].pt_kp->ki_comm; wmove(wnd, y, 0); wclrtoeol(wnd); mvwaddstr(wnd, y, 0, uname); snprintf(pidname, sizeof(pidname), "%10.10s", pname); mvwaddstr(wnd, y, 9, pidname); wmove(wnd, y, 20); for (j = pt[k].pt_pctcpu * 50 + 0.5; j > 0; j--) waddch(wnd, 'X'); } wmove(wnd, y, 0); wclrtobot(wnd); } int initpigs(void) { fixpt_t ccpu; size_t len; int err; len = sizeof(ccpu); err = sysctlbyname("kern.ccpu", &ccpu, &len, NULL, 0); if (err || len != sizeof(ccpu)) { perror("kern.ccpu"); return (0); } len = sizeof(fscale); err = sysctlbyname("kern.fscale", &fscale, &len, NULL, 0); if (err || len != sizeof(fscale)) { perror("kern.fscale"); return (0); } lccpu = log((double) ccpu / fscale); return(1); } void fetchpigs(void) { int i; float ftime; float *pctp; struct kinfo_proc *kpp; static int maxnproc = 0; if ((kpp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc)) == NULL) { error("%s", kvm_geterr(kd)); nproc = 0; return; } if (nproc > maxnproc) { if ((pt = realloc(pt, nproc * sizeof(*pt))) == NULL) { error("Out of memory"); die(0); } maxnproc = nproc; } /* * calculate %cpu for each proc */ for (i = 0; i < nproc; i++) { pt[i].pt_kp = &kpp[i]; pctp = &pt[i].pt_pctcpu; ftime = kpp[i].ki_swtime; if (ftime == 0 || (kpp[i].ki_flag & P_INMEM) == 0) *pctp = 0; else *pctp = ((double) kpp[i].ki_pctcpu / fscale) / (1.0 - exp(ftime * lccpu)); } } void labelpigs(void) { wmove(wnd, 0, 0); wclrtoeol(wnd); mvwaddstr(wnd, 0, 20, "/0% /10 /20 /30 /40 /50 /60 /70 /80 /90 /100"); } int compar(const void *a, const void *b) { return (((const struct p_times *) a)->pt_pctcpu > ((const struct p_times *) b)->pt_pctcpu)? -1: 1; } diff --git a/usr.bin/systat/sctp.c b/usr.bin/systat/sctp.c index 928472dbfdc5..abe4d0e68e04 100644 --- a/usr.bin/systat/sctp.c +++ b/usr.bin/systat/sctp.c @@ -1,355 +1,354 @@ /*- * Copyright (c) 2015 * The Regents of the University of California. All rights reserved. * Michael Tuexen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include "systat.h" #include "extern.h" #include "mode.h" static struct sctpstat curstat, initstat, oldstat; /*- --0 1 2 3 4 5 6 7 --0123456789012345678901234567890123456789012345678901234567890123456789012345 00 SCTP Associations SCTP Packets 01999999999999 associations initiated 999999999999 packets sent 02999999999999 associations accepted 999999999999 packets received 03999999999999 associations restarted 999999999999 - out of the blue 04999999999999 associations terminated 999999999999 - bad vtag 05999999999999 associations aborted 999999999999 - bad crc32c 06 07 SCTP Timers SCTP Chunks 08999999999999 init timeouts 999999999999 control chunks sent 09999999999999 cookie timeouts 999999999999 data chunks sent 10999999999999 data timeouts 999999999999 - ordered 11999999999999 delayed sack timeouts 999999999999 - unordered 12999999999999 shutdown timeouts 999999999999 control chunks received 13999999999999 shutdown-ack timeouts 999999999999 data chunks received 14999999999999 shutdown guard timeouts 999999999999 - ordered 15999999999999 heartbeat timeouts 999999999999 - unordered 16999999999999 path MTU timeouts 17999999999999 autoclose timeouts SCTP user messages 18999999999999 asconf timeouts 999999999999 fragmented 19999999999999 stream reset timeouts 999999999999 reassembled --0123456789012345678901234567890123456789012345678901234567890123456789012345 --0 1 2 3 4 5 6 7 */ WINDOW * opensctp(void) { return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0)); } void closesctp(WINDOW *w) { if (w != NULL) { wclear(w); wrefresh(w); delwin(w); } } void labelsctp(void) { wmove(wnd, 0, 0); wclrtoeol(wnd); #define L(row, str) mvwprintw(wnd, row, 13, str) #define R(row, str) mvwprintw(wnd, row, 51, str); L(0, "SCTP Associations"); R(0, "SCTP Packets"); L(1, "associations initiated"); R(1, "packets sent"); L(2, "associations accepted"); R(2, "packets received"); L(3, "associations restarted"); R(3, "- out of the blue"); L(4, "associations terminated"); R(4, "- bad vtag"); L(5, "associations aborted"); R(5, "- bad crc32c"); L(7, "SCTP Timers"); R(7, "SCTP Chunks"); L(8, "init timeouts"); R(8, "control chunks sent"); L(9, "cookie timeouts"); R(9, "data chunks sent"); L(10, "data timeouts"); R(10, "- ordered"); L(11, "delayed sack timeouts"); R(11, "- unordered"); L(12, "shutdown timeouts"); R(12, "control chunks received"); L(13, "shutdown-ack timeouts"); R(13, "data chunks received"); L(14, "shutdown guard timeouts"); R(14, "- ordered"); L(15, "heartbeat timeouts"); R(15, "- unordered"); L(16, "path MTU timeouts"); L(17, "autoclose timeouts"); R(17, "SCTP User Messages"); L(18, "asconf timeouts"); R(18, "fragmented"); L(19, "stream reset timeouts"); R(19, "reassembled"); #undef L #undef R } static void domode(struct sctpstat *ret) { const struct sctpstat *sub; int divisor = 1; switch(currentmode) { case display_RATE: sub = &oldstat; divisor = (delay > 1000000) ? delay / 1000000 : 1; break; case display_DELTA: sub = &oldstat; break; case display_SINCE: sub = &initstat; break; default: *ret = curstat; return; } #define DO(stat) ret->stat = (curstat.stat - sub->stat) / divisor DO(sctps_currestab); DO(sctps_activeestab); DO(sctps_restartestab); DO(sctps_collisionestab); DO(sctps_passiveestab); DO(sctps_aborted); DO(sctps_shutdown); DO(sctps_outoftheblue); DO(sctps_checksumerrors); DO(sctps_outcontrolchunks); DO(sctps_outorderchunks); DO(sctps_outunorderchunks); DO(sctps_incontrolchunks); DO(sctps_inorderchunks); DO(sctps_inunorderchunks); DO(sctps_fragusrmsgs); DO(sctps_reasmusrmsgs); DO(sctps_outpackets); DO(sctps_inpackets); DO(sctps_recvpackets); DO(sctps_recvdatagrams); DO(sctps_recvpktwithdata); DO(sctps_recvsacks); DO(sctps_recvdata); DO(sctps_recvdupdata); DO(sctps_recvheartbeat); DO(sctps_recvheartbeatack); DO(sctps_recvecne); DO(sctps_recvauth); DO(sctps_recvauthmissing); DO(sctps_recvivalhmacid); DO(sctps_recvivalkeyid); DO(sctps_recvauthfailed); DO(sctps_recvexpress); DO(sctps_recvexpressm); DO(sctps_recvswcrc); DO(sctps_recvhwcrc); DO(sctps_sendpackets); DO(sctps_sendsacks); DO(sctps_senddata); DO(sctps_sendretransdata); DO(sctps_sendfastretrans); DO(sctps_sendmultfastretrans); DO(sctps_sendheartbeat); DO(sctps_sendecne); DO(sctps_sendauth); DO(sctps_senderrors); DO(sctps_sendswcrc); DO(sctps_sendhwcrc); DO(sctps_pdrpfmbox); DO(sctps_pdrpfehos); DO(sctps_pdrpmbda); DO(sctps_pdrpmbct); DO(sctps_pdrpbwrpt); DO(sctps_pdrpcrupt); DO(sctps_pdrpnedat); DO(sctps_pdrppdbrk); DO(sctps_pdrptsnnf); DO(sctps_pdrpdnfnd); DO(sctps_pdrpdiwnp); DO(sctps_pdrpdizrw); DO(sctps_pdrpbadd); DO(sctps_pdrpmark); DO(sctps_timoiterator); DO(sctps_timodata); DO(sctps_timowindowprobe); DO(sctps_timoinit); DO(sctps_timosack); DO(sctps_timoshutdown); DO(sctps_timoheartbeat); DO(sctps_timocookie); DO(sctps_timosecret); DO(sctps_timopathmtu); DO(sctps_timoshutdownack); DO(sctps_timoshutdownguard); DO(sctps_timostrmrst); DO(sctps_timoearlyfr); DO(sctps_timoasconf); DO(sctps_timodelprim); DO(sctps_timoautoclose); DO(sctps_timoassockill); DO(sctps_timoinpkill); DO(sctps_hdrops); DO(sctps_badsum); DO(sctps_noport); DO(sctps_badvtag); DO(sctps_badsid); DO(sctps_nomem); DO(sctps_fastretransinrtt); DO(sctps_markedretrans); DO(sctps_naglesent); DO(sctps_naglequeued); DO(sctps_maxburstqueued); DO(sctps_ifnomemqueued); DO(sctps_windowprobed); DO(sctps_lowlevelerr); DO(sctps_lowlevelerrusr); DO(sctps_datadropchklmt); DO(sctps_datadroprwnd); DO(sctps_ecnereducedcwnd); DO(sctps_vtagexpress); DO(sctps_vtagbogus); DO(sctps_primary_randry); DO(sctps_cmt_randry); DO(sctps_slowpath_sack); DO(sctps_wu_sacks_sent); DO(sctps_sends_with_flags); DO(sctps_sends_with_unord); DO(sctps_sends_with_eof); DO(sctps_sends_with_abort); DO(sctps_protocol_drain_calls); DO(sctps_protocol_drains_done); DO(sctps_read_peeks); DO(sctps_cached_chk); DO(sctps_cached_strmoq); DO(sctps_left_abandon); DO(sctps_send_burst_avoid); DO(sctps_send_cwnd_avoid); DO(sctps_fwdtsn_map_over); DO(sctps_queue_upd_ecne); #undef DO } void showsctp(void) { struct sctpstat stats; memset(&stats, 0, sizeof stats); domode(&stats); #define DO(stat, row, col) \ mvwprintw(wnd, row, col, "%12lu", stats.stat) #define L(row, stat) DO(stat, row, 0) #define R(row, stat) DO(stat, row, 38) L(1, sctps_activeestab); R(1, sctps_outpackets); L(2, sctps_passiveestab); R(2, sctps_inpackets); L(3, sctps_restartestab); R(3, sctps_outoftheblue); L(4, sctps_shutdown); R(4, sctps_badvtag); L(5, sctps_aborted); R(5, sctps_checksumerrors); L(8, sctps_timoinit); R(8, sctps_outcontrolchunks); L(9, sctps_timocookie); R(9, sctps_senddata); L(10, sctps_timodata); R(10, sctps_outorderchunks); L(11, sctps_timosack); R(11, sctps_outunorderchunks); L(12, sctps_timoshutdown); R(12, sctps_incontrolchunks); L(13, sctps_timoshutdownack); R(13, sctps_recvdata); L(14, sctps_timoshutdownguard); R(14, sctps_inorderchunks); L(15, sctps_timoheartbeat); R(15, sctps_inunorderchunks); L(16, sctps_timopathmtu); L(17, sctps_timoautoclose); L(18, sctps_timoasconf); R(18, sctps_fragusrmsgs); L(19, sctps_timostrmrst); R(19, sctps_reasmusrmsgs); #undef DO #undef L #undef R } int initsctp(void) { size_t len; const char *name = "net.inet.sctp.stats"; len = 0; if (sysctlbyname(name, NULL, &len, NULL, 0) < 0) { error("sysctl getting sctpstat size failed"); return 0; } if (len > sizeof curstat) { error("sctpstat structure has grown--recompile systat!"); return 0; } if (sysctlbyname(name, &initstat, &len, NULL, 0) < 0) { error("sysctl getting sctpstat failed"); return 0; } oldstat = initstat; return 1; } void resetsctp(void) { size_t len; const char *name = "net.inet.sctp.stats"; len = sizeof initstat; if (sysctlbyname(name, &initstat, &len, NULL, 0) < 0) { error("sysctl getting sctpstat failed"); } oldstat = initstat; } void fetchsctp(void) { size_t len; const char *name = "net.inet.sctp.stats"; oldstat = curstat; len = sizeof curstat; if (sysctlbyname(name, &curstat, &len, NULL, 0) < 0) { error("sysctl getting sctpstat failed"); } return; } diff --git a/usr.bin/systat/sysput.c b/usr.bin/systat/sysput.c index 78d681158766..4053037236f4 100644 --- a/usr.bin/systat/sysput.c +++ b/usr.bin/systat/sysput.c @@ -1,115 +1,114 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019, 2020 Yoshihiro Ota * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include "systat.h" #include "extern.h" void sysputspaces(WINDOW *wd, int row, int lcol, int width) { static char str60[] = " " " "; mvwaddstr(wd, row, lcol, str60 + sizeof(str60) - width - 1); } void sysputstrs(WINDOW *wd __unused, int row, int lcol, int width) { static char str60[] = "********************" "****************************************"; /* * XXX wnd instead of wd? */ mvwaddstr(wnd, row, lcol, str60 + sizeof(str60) - width - 1); } void sysputXs(WINDOW *wd __unused, int row, int lcol, int width) { static char str60[] = "XXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; /* * XXX wnd instead of wd? */ mvwaddstr(wnd, row, lcol, str60 + sizeof(str60) - width - 1); } void sysputuint64(WINDOW *wd, int row, int lcol, int width, uint64_t val, int flags) { char *start, wrtbuf[width + width + 1]; int len; start = wrtbuf; flags |= HN_NOSPACE; if (val > INT64_MAX) goto error; else len = humanize_number(&wrtbuf[width], width + 1, val, "", HN_AUTOSCALE, flags); if (len < 0) goto error; else if (len < width) memset(wrtbuf + len, ' ', width - len); start += len; mvwaddstr(wd, row, lcol, start); return; error: sysputstrs(wd, row, lcol, width); } void sysputwuint64(WINDOW *wd, int row, int lcol, int width, uint64_t val, int flags) { if(val == 0) sysputspaces(wd, row, lcol, width); else sysputuint64(wd, row, lcol, width, val, flags); } void sysputpage(WINDOW *wd, int row, int lcol, int width, uint64_t pages, int flags) { sysputuint64(wd, row, lcol, width, ptoa(pages), flags); } diff --git a/usr.bin/systat/tcp.c b/usr.bin/systat/tcp.c index 6f90efcf53b3..83163a3525a8 100644 --- a/usr.bin/systat/tcp.c +++ b/usr.bin/systat/tcp.c @@ -1,320 +1,319 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "systat.h" #include "extern.h" #include "mode.h" static struct tcpstat curstat, initstat, oldstat; /*- --0 1 2 3 4 5 6 7 --0123456789012345678901234567890123456789012345678901234567890123456789012345 00 TCP Connections TCP Packets 01999999999999 connections initiated 999999999999 total packets sent 02999999999999 connections accepted 999999999999 - data 03999999999999 connections established 999999999999 - data (retransmit by dupack) 04999999999999 connections dropped 999999999999 - data (retransmit by sack) 05999999999999 - in embryonic state 999999999999 - ack-only 06999999999999 - on retransmit timeout 999999999999 - window probes 07999999999999 - by keepalive 999999999999 - window updates 08999999999999 - from listen queue 999999999999 - urgent data only 09 999999999999 - control 10 999999999999 - resends by PMTU discovery 11 TCP Timers 999999999999 total packets received 12999999999999 potential rtt updates 999999999999 - in sequence 13999999999999 - successful 999999999999 - completely duplicate 14999999999999 delayed acks sent 999999999999 - with some duplicate data 15999999999999 retransmit timeouts 999999999999 - out-of-order 16999999999999 persist timeouts 999999999999 - duplicate acks 17999999999999 keepalive probes 999999999999 - acks 18999999999999 - timeouts 999999999999 - window probes 19 999999999999 - window updates 20 999999999999 - bad checksum --0123456789012345678901234567890123456789012345678901234567890123456789012345 --0 1 2 3 4 5 6 7 */ WINDOW * opentcp(void) { return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0)); } void closetcp(WINDOW *w) { if (w == NULL) return; wclear(w); wrefresh(w); delwin(w); } void labeltcp(void) { wmove(wnd, 0, 0); wclrtoeol(wnd); #define L(row, str) mvwprintw(wnd, row, 13, str) #define R(row, str) mvwprintw(wnd, row, 51, str); L(0, "TCP Connections"); R(0, "TCP Packets"); L(1, "connections initiated"); R(1, "total packets sent"); L(2, "connections accepted"); R(2, "- data"); L(3, "connections established"); R(3, "- data (retransmit by dupack)"); L(4, "connections dropped"); R(4, "- data (retransmit by sack)"); L(5, "- in embryonic state"); R(5, "- ack-only"); L(6, "- on retransmit timeout"); R(6, "- window probes"); L(7, "- by keepalive"); R(7, "- window updates"); L(8, "- exceeded progress time"); R(8, "- urgent data only"); L(9, "- from listen queue"); R(9, "- control"); R(10, "- resends by PMTU discovery"); L(11, "TCP Timers"); R(11, "total packets received"); L(12, "potential rtt updates"); R(12, "- in sequence"); L(13, "- successful"); R(13, "- completely duplicate"); L(14, "delayed acks sent"); R(14, "- with some duplicate data"); L(15, "retransmit timeouts"); R(15, "- out-of-order"); L(16, "persist timeouts"); R(16, "- duplicate acks"); L(17, "keepalive probes"); R(17, "- acks"); L(18, "- timeouts"); R(18, "- window probes"); R(19, "- window updates"); R(20, "- bad checksum"); #undef L #undef R } static void domode(struct tcpstat *ret) { const struct tcpstat *sub; int divisor = 1; switch(currentmode) { case display_RATE: sub = &oldstat; divisor = (delay > 1000000) ? delay / 1000000 : 1; break; case display_DELTA: sub = &oldstat; break; case display_SINCE: sub = &initstat; break; default: *ret = curstat; return; } #define DO(stat) ret->stat = (curstat.stat - sub->stat) / divisor DO(tcps_connattempt); DO(tcps_accepts); DO(tcps_connects); DO(tcps_drops); DO(tcps_conndrops); DO(tcps_closed); DO(tcps_segstimed); DO(tcps_rttupdated); DO(tcps_delack); DO(tcps_timeoutdrop); DO(tcps_rexmttimeo); DO(tcps_persisttimeo); DO(tcps_keeptimeo); DO(tcps_keepprobe); DO(tcps_keepdrops); DO(tcps_progdrops); DO(tcps_sndtotal); DO(tcps_sndpack); DO(tcps_sndbyte); DO(tcps_sndrexmitpack); DO(tcps_sack_rexmits); DO(tcps_sndacks); DO(tcps_sndprobe); DO(tcps_sndurg); DO(tcps_sndwinup); DO(tcps_sndctrl); DO(tcps_rcvtotal); DO(tcps_rcvpack); DO(tcps_rcvbyte); DO(tcps_rcvbadsum); DO(tcps_rcvbadoff); DO(tcps_rcvshort); DO(tcps_rcvduppack); DO(tcps_rcvdupbyte); DO(tcps_rcvpartduppack); DO(tcps_rcvpartdupbyte); DO(tcps_rcvoopack); DO(tcps_rcvoobyte); DO(tcps_rcvpackafterwin); DO(tcps_rcvbyteafterwin); DO(tcps_rcvafterclose); DO(tcps_rcvwinprobe); DO(tcps_rcvdupack); DO(tcps_rcvacktoomuch); DO(tcps_rcvackpack); DO(tcps_rcvackbyte); DO(tcps_rcvwinupd); DO(tcps_pawsdrop); DO(tcps_predack); DO(tcps_preddat); DO(tcps_pcbcachemiss); DO(tcps_cachedrtt); DO(tcps_cachedrttvar); DO(tcps_cachedssthresh); DO(tcps_usedrtt); DO(tcps_usedrttvar); DO(tcps_usedssthresh); DO(tcps_persistdrop); DO(tcps_badsyn); DO(tcps_mturesent); DO(tcps_listendrop); #undef DO } void showtcp(void) { struct tcpstat stats; memset(&stats, 0, sizeof stats); domode(&stats); #define DO(stat, row, col) \ mvwprintw(wnd, row, col, "%12"PRIu64, stats.stat) #define L(row, stat) DO(stat, row, 0) #define R(row, stat) DO(stat, row, 38) L(1, tcps_connattempt); R(1, tcps_sndtotal); L(2, tcps_accepts); R(2, tcps_sndpack); L(3, tcps_connects); R(3, tcps_sndrexmitpack); L(4, tcps_drops); R(4, tcps_sack_rexmits); L(5, tcps_conndrops); R(5, tcps_sndacks); L(6, tcps_timeoutdrop); R(6, tcps_sndprobe); L(7, tcps_keepdrops); R(7, tcps_sndwinup); L(8, tcps_progdrops); R(8, tcps_sndurg); L(9, tcps_listendrop); R(9, tcps_sndctrl); R(10, tcps_mturesent); R(11, tcps_rcvtotal); L(12, tcps_segstimed); R(12, tcps_rcvpack); L(13, tcps_rttupdated); R(13, tcps_rcvduppack); L(14, tcps_delack); R(14, tcps_rcvpartduppack); L(15, tcps_rexmttimeo); R(15, tcps_rcvoopack); L(16, tcps_persisttimeo); R(16, tcps_rcvdupack); L(17, tcps_keepprobe); R(17, tcps_rcvackpack); L(18, tcps_keeptimeo); R(18, tcps_rcvwinprobe); R(19, tcps_rcvwinupd); R(20, tcps_rcvbadsum); #undef DO #undef L #undef R } int inittcp(void) { size_t len; int name[4]; name[0] = CTL_NET; name[1] = PF_INET; name[2] = IPPROTO_TCP; name[3] = TCPCTL_STATS; len = 0; if (sysctl(name, 4, 0, &len, 0, 0) < 0) { error("sysctl getting tcpstat size failed"); return 0; } if (len > sizeof curstat) { error("tcpstat structure has grown--recompile systat!"); return 0; } if (sysctl(name, 4, &initstat, &len, 0, 0) < 0) { error("sysctl getting tcpstat failed"); return 0; } oldstat = initstat; return 1; } void resettcp(void) { size_t len; int name[4]; name[0] = CTL_NET; name[1] = PF_INET; name[2] = IPPROTO_TCP; name[3] = TCPCTL_STATS; len = sizeof initstat; if (sysctl(name, 4, &initstat, &len, 0, 0) < 0) { error("sysctl getting tcpstat failed"); } oldstat = initstat; } void fetchtcp(void) { int name[4]; size_t len; oldstat = curstat; name[0] = CTL_NET; name[1] = PF_INET; name[2] = IPPROTO_TCP; name[3] = TCPCTL_STATS; len = sizeof curstat; if (sysctl(name, 4, &curstat, &len, 0, 0) < 0) return; } diff --git a/usr.bin/systat/zarc.c b/usr.bin/systat/zarc.c index f2971094cdbe..5f9cb8cc1130 100644 --- a/usr.bin/systat/zarc.c +++ b/usr.bin/systat/zarc.c @@ -1,237 +1,236 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2014 - 2017, 2019 Yoshihiro Ota * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include "systat.h" #include "extern.h" #include "devs.h" struct zfield { uint64_t arcstats; uint64_t arcstats_demand_data; uint64_t arcstats_demand_metadata; uint64_t arcstats_prefetch_data; uint64_t arcstats_prefetch_metadata; uint64_t zfetchstats; uint64_t arcstats_l2; }; static struct zarcstats { struct zfield hits; struct zfield misses; } curstat, initstat, oldstat; struct zarcrates { struct zfield current; struct zfield total; }; static void getinfo(struct zarcstats *ls); WINDOW * openzarc(void) { return (subwin(stdscr, LINES - 3 - 1, 0, MAINWIN_ROW, 0)); } void closezarc(WINDOW *w) { if (w == NULL) return; wclear(w); wrefresh(w); delwin(w); } void labelzarc(void) { int row = 1; wmove(wnd, 0, 0); wclrtoeol(wnd); mvwprintw(wnd, 0, 31+1, "%4.4s %6.6s %6.6s | Total %4.4s %6.6s %6.6s", "Rate", "Hits", "Misses", "Rate", "Hits", "Misses"); #define L(str) mvwprintw(wnd, row++, 5, \ "%-26.26s: %% | %%", #str) L(arcstats); L(arcstats.demand_data); L(arcstats.demand_metadata); L(arcstats.prefetch_data); L(arcstats.prefetch_metadata); L(zfetchstats); L(arcstats.l2); #undef L dslabel(12, 0, 18); } static int calc_rate(uint64_t hits, uint64_t misses) { if(hits) return 100 * hits / (hits + misses); else return 0; } static void domode(struct zarcstats *delta, struct zarcrates *rate) { #define DO(stat) \ delta->hits.stat = (curstat.hits.stat - oldstat.hits.stat); \ delta->misses.stat = (curstat.misses.stat - oldstat.misses.stat); \ rate->current.stat = calc_rate(delta->hits.stat, delta->misses.stat); \ rate->total.stat = calc_rate(curstat.hits.stat, curstat.misses.stat) DO(arcstats); DO(arcstats_demand_data); DO(arcstats_demand_metadata); DO(arcstats_prefetch_data); DO(arcstats_prefetch_metadata); DO(zfetchstats); DO(arcstats_l2); DO(arcstats); DO(arcstats_demand_data); DO(arcstats_demand_metadata); DO(arcstats_prefetch_data); DO(arcstats_prefetch_metadata); DO(zfetchstats); DO(arcstats_l2); #undef DO } void showzarc(void) { int row = 1; struct zarcstats delta = {}; struct zarcrates rate = {}; domode(&delta, &rate); #define DO(stat, col, width) \ sysputuint64(wnd, row, col, width, stat, HN_DIVISOR_1000) #define RATES(stat) mvwprintw(wnd, row, 31+1, "%3"PRIu64, rate.current.stat);\ mvwprintw(wnd, row, 31+1+5+7+7+8, "%3"PRIu64, rate.total.stat) #define HITS(stat) DO(delta.hits.stat, 31+1+5, 6); \ DO(curstat.hits.stat, 31+1+5+7+7+8+5, 6) #define MISSES(stat) DO(delta.misses.stat, 31+1+5+7, 6); \ DO(curstat.misses.stat, 31+1+5+7+7+8+5+7, 6) #define E(stat) RATES(stat); HITS(stat); MISSES(stat); ++row E(arcstats); E(arcstats_demand_data); E(arcstats_demand_metadata); E(arcstats_prefetch_data); E(arcstats_prefetch_metadata); E(zfetchstats); E(arcstats_l2); #undef DO #undef E #undef MISSES #undef HITS #undef RATES dsshow(12, 0, 18, &cur_dev, &last_dev); } int initzarc(void) { dsinit(12); getinfo(&initstat); curstat = oldstat = initstat; return 1; } void resetzarc(void) { initzarc(); } static void getinfo(struct zarcstats *ls) { struct devinfo *tmp_dinfo; tmp_dinfo = last_dev.dinfo; last_dev.dinfo = cur_dev.dinfo; cur_dev.dinfo = tmp_dinfo; last_dev.snap_time = cur_dev.snap_time; dsgetinfo(&cur_dev); size_t size = sizeof(ls->hits.arcstats); if (sysctlbyname("kstat.zfs.misc.arcstats.hits", &ls->hits.arcstats, &size, NULL, 0) != 0) return; GETSYSCTL("kstat.zfs.misc.arcstats.misses", ls->misses.arcstats); GETSYSCTL("kstat.zfs.misc.arcstats.demand_data_hits", ls->hits.arcstats_demand_data); GETSYSCTL("kstat.zfs.misc.arcstats.demand_data_misses", ls->misses.arcstats_demand_data); GETSYSCTL("kstat.zfs.misc.arcstats.demand_metadata_hits", ls->hits.arcstats_demand_metadata); GETSYSCTL("kstat.zfs.misc.arcstats.demand_metadata_misses", ls->misses.arcstats_demand_metadata); GETSYSCTL("kstat.zfs.misc.arcstats.prefetch_data_hits", ls->hits.arcstats_prefetch_data); GETSYSCTL("kstat.zfs.misc.arcstats.prefetch_data_misses", ls->misses.arcstats_prefetch_data); GETSYSCTL("kstat.zfs.misc.arcstats.prefetch_metadata_hits", ls->hits.arcstats_prefetch_metadata); GETSYSCTL("kstat.zfs.misc.arcstats.prefetch_metadata_misses", ls->misses.arcstats_prefetch_metadata); GETSYSCTL("kstat.zfs.misc.zfetchstats.hits", ls->hits.zfetchstats); GETSYSCTL("kstat.zfs.misc.zfetchstats.misses", ls->misses.zfetchstats); GETSYSCTL("kstat.zfs.misc.arcstats.l2_hits", ls->hits.arcstats_l2); GETSYSCTL("kstat.zfs.misc.arcstats.l2_misses", ls->misses.arcstats_l2); } void fetchzarc(void) { oldstat = curstat; getinfo(&curstat); } diff --git a/usr.bin/tabs/tabs.c b/usr.bin/tabs/tabs.c index 7aa50472c343..0cc942565ab3 100644 --- a/usr.bin/tabs/tabs.c +++ b/usr.bin/tabs/tabs.c @@ -1,237 +1,236 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2002 Tim J. Robbins. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * tabs -- set terminal tabs * * This utility displays a series of characters that clears the terminal * hardware tab settings, then initialises them to specified values, * and optionally sets a soft margin. */ -#include #include #include #include #include #include #include #include #include #include #include #include /* Maximum number of tab stops allowed in table. */ #define NSTOPS 20 #define NELEMS(a) (sizeof(a) / sizeof(a[0])) /* Predefined formats, taken from IEEE Std 1003.1-2001. */ static const struct { const char *name; /* Format name used on cmd. line */ long stops[NSTOPS]; /* Column positions */ } formats[] = { { "a", { 1, 10, 16, 36, 72 } }, { "a2", { 1, 10, 16, 40, 72 } }, { "c", { 1, 8, 12, 16, 20, 55 } }, { "c2", { 1, 6, 10, 14, 49 } }, { "c3", { 1, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 62, 67 } }, { "f", { 1, 7, 11, 15, 19, 23 } }, { "p", { 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61 } }, { "s", { 1, 10, 55 } }, { "u", { 1, 12, 20, 44 } } }; static void gettabs(char *, long *, long *); static int ttywidth(void); static void usage(void); int main(int argc __unused, char *argv[]) { long cols, i, inc, j, margin, nstops, stops[NSTOPS]; const char *cr, *ct, *st, *ML; char area[1024], *ap, *arg, *end; setlocale(LC_ALL, ""); inc = 8; margin = 0; nstops = -1; while ((arg = *++argv) != NULL && (*arg == '-' || *arg == '+')) { if (*arg == '+') { /* +m[n] or +[n] */ if (*++arg == 'm') arg++; if (*arg != '\0') { errno = 0; margin = strtol(arg, &end, 10); if (errno != 0 || *end != '\0' || margin < 0) errx(1, "%s: invalid margin width", arg); } else margin = 10; } else if (isdigit(arg[1])) { /* -n */ errno = 0; inc = strtol(arg + 1, &end, 10); if (errno != 0 || *end != '\0' || inc < 0) errx(1, "%s: invalid increment", arg + 1); } else if (arg[1] == 'T') { /* -Ttype or -T type */ if (arg[2] != '\0') setenv("TERM", arg + 2, 1); else { if ((arg = *++argv) == NULL) usage(); setenv("TERM", arg, 1); } } else if (arg[1] == '-') { arg = *++argv; break; } else { /* Predefined format */ for (i = 0; i < (int)NELEMS(formats); i++) if (strcmp(formats[i].name, arg + 1) == 0) break; if (i == NELEMS(formats)) usage(); for (j = nstops = 0; j < NSTOPS && formats[i].stops[j] != 0; j++) stops[nstops++] = formats[i].stops[j]; } } if (arg != NULL) { if (nstops != -1) usage(); gettabs(arg, stops, &nstops); } /* Initialise terminal, get the strings we need */ setupterm(NULL, 1, NULL); ap = area; if ((ct = tgetstr("ct", &ap)) == NULL) errx(1, "terminal cannot clear tabs"); if ((st = tgetstr("st", &ap)) == NULL) errx(1, "terminal cannot set tabs"); if ((cr = tgetstr("cr", &ap)) == NULL) cr = "\r"; ML = tgetstr("ML", &ap); cols = ttywidth(); /* Clear all tabs. */ putp(cr); putp(ct); /* * Set soft margin. * XXX Does this actually work? */ if (ML != NULL) { printf("%*s", (int)margin, ""); putp(ML); } else if (margin != 0) warnx("terminal cannot set left margin"); /* Optionally output new tab stops. */ if (nstops >= 0) { printf("%*s", (int)stops[0] - 1, ""); putp(st); for (i = 1; i < nstops; i++) { printf("%*s", (int)(stops[i] - stops[i - 1]), ""); putp(st); } } else if (inc > 0) { for (i = 0; i < cols / inc; i++) { putp(st); printf("%*s", (int)inc, ""); } putp(st); } putp(cr); exit(0); } static void usage(void) { fprintf(stderr, "usage: tabs [-n|-a|-a2|-c|-c2|-c3|-f|-p|-s|-u] [+m[n]] [-T type]\n"); fprintf(stderr, " tabs [-T type] [+[n]] n1,[n2,...]\n"); exit(1); } static void gettabs(char *arg, long stops[], long *nstops) { char *tok, *end; long last, stop; for (last = *nstops = 0, tok = strtok(arg, ","); tok != NULL; tok = strtok(NULL, ",")) { if (*nstops >= NSTOPS) errx(1, "too many tab stops (limit %d)", NSTOPS); errno = 0; stop = strtol(tok, &end, 10); if (errno != 0 || *end != '\0' || stop <= 0) errx(1, "%s: invalid tab stop", tok); if (*tok == '+') { if (tok == arg) errx(1, "%s: first tab may not be relative", tok); stop += last; } if (last > stop) errx(1, "cannot go backwards"); last = stops[(*nstops)++] = stop; } } static int ttywidth(void) { struct winsize ws; int width; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) width = ws.ws_col; else if ((width = tgetnum("co")) == 0) { width = 80; warnx("cannot find terminal width; defaulted to %d", width); } return (width); } diff --git a/usr.bin/tail/reverse.c b/usr.bin/tail/reverse.c index 419ffb3db804..8be76eff9648 100644 --- a/usr.bin/tail/reverse.c +++ b/usr.bin/tail/reverse.c @@ -1,284 +1,280 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Edward Sze-Tyan Wang. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" static void r_buf(FILE *, const char *); static void r_reg(FILE *, const char *, enum STYLE, off_t, struct stat *); /* * reverse -- display input in reverse order by line. * * There are six separate cases for this -- regular and non-regular * files by bytes, lines or the whole file. * * BYTES display N bytes * REG mmap the file and display the lines * NOREG cyclically read characters into a wrap-around buffer * * LINES display N lines * REG mmap the file and display the lines * NOREG cyclically read lines into a wrap-around array of buffers * * FILE display the entire file * REG mmap the file and display the lines * NOREG cyclically read input into a linked list of buffers */ void reverse(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp) { if (style != REVERSE && off == 0) return; if (S_ISREG(sbp->st_mode)) r_reg(fp, fn, style, off, sbp); else switch(style) { case FBYTES: case RBYTES: bytes(fp, fn, off); break; case FLINES: case RLINES: lines(fp, fn, off); break; case REVERSE: r_buf(fp, fn); break; default: break; } } /* * r_reg -- display a regular file in reverse order by line. */ static void r_reg(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp) { struct mapinfo map; off_t curoff, size, lineend; int i; if (!(size = sbp->st_size)) return; map.start = NULL; map.mapoff = map.maxoff = size; map.fd = fileno(fp); map.maplen = 0; /* * Last char is special, ignore whether newline or not. Note that * size == 0 is dealt with above, and size == 1 sets curoff to -1. */ curoff = size - 2; lineend = size; while (curoff >= 0) { if (curoff < map.mapoff || curoff >= map.mapoff + (off_t)map.maplen) { if (maparound(&map, curoff) != 0) { ierr(fn); return; } } for (i = curoff - map.mapoff; i >= 0; i--) { if (style == RBYTES && --off == 0) break; if (map.start[i] == '\n') break; } /* `i' is either the map offset of a '\n', or -1. */ curoff = map.mapoff + i; if (i < 0) continue; /* Print the line and update offsets. */ if (mapprint(&map, curoff + 1, lineend - curoff - 1) != 0) { ierr(fn); return; } lineend = curoff + 1; curoff--; if (style == RLINES) off--; if (off == 0 && style != REVERSE) { /* Avoid printing anything below. */ curoff = 0; break; } } if (curoff < 0 && mapprint(&map, 0, lineend) != 0) { ierr(fn); return; } if (map.start != NULL && munmap(map.start, map.maplen)) ierr(fn); } #define BSZ (128 * 1024) typedef struct bfelem { TAILQ_ENTRY(bfelem) entries; size_t len; char l[BSZ]; } bfelem_t; /* * r_buf -- display a non-regular file in reverse order by line. * * This is the function that saves the entire input, storing the data in a * doubly linked list of buffers and then displays them in reverse order. * It has the usual nastiness of trying to find the newlines, as there's no * guarantee that a newline occurs anywhere in the file, let alone in any * particular buffer. If we run out of memory, input is discarded (and the * user warned). */ static void r_buf(FILE *fp, const char *fn) { struct bfelem *tl, *first = NULL; size_t llen; char *p; off_t enomem = 0; TAILQ_HEAD(bfhead, bfelem) head; TAILQ_INIT(&head); while (!feof(fp)) { size_t len; /* * Allocate a new block and link it into place in a doubly * linked list. If out of memory, toss the LRU block and * keep going. */ while ((tl = malloc(sizeof(bfelem_t))) == NULL) { first = TAILQ_FIRST(&head); if (TAILQ_EMPTY(&head)) err(1, "malloc"); enomem += first->len; TAILQ_REMOVE(&head, first, entries); free(first); } TAILQ_INSERT_TAIL(&head, tl, entries); /* Fill the block with input data. */ len = 0; while ((!feof(fp)) && len < BSZ) { p = tl->l + len; len += fread(p, 1, BSZ - len, fp); if (ferror(fp)) { ierr(fn); return; } } tl->len = len; } if (enomem) { warnx("warning: %jd bytes discarded", (intmax_t)enomem); rval = 1; } /* * Now print the lines in reverse order * Outline: * Scan backward for "\n", * print forward to the end of the buffers * free any buffers that start after the "\n" just found * Loop */ tl = TAILQ_LAST(&head, bfhead); first = TAILQ_FIRST(&head); while (tl != NULL) { struct bfelem *temp; for (p = tl->l + tl->len - 1, llen = 0; p >= tl->l; --p, ++llen) { int start = (tl == first && p == tl->l); if ((*p == '\n') || start) { struct bfelem *tr; if (llen && start && *p != '\n') WR(p, llen + 1); else if (llen) { WR(p + 1, llen); if (start && *p == '\n') WR(p, 1); } tr = TAILQ_NEXT(tl, entries); llen = 0; if (tr != NULL) { TAILQ_FOREACH_FROM_SAFE(tr, &head, entries, temp) { if (tr->len) WR(&tr->l, tr->len); TAILQ_REMOVE(&head, tr, entries); free(tr); } } } } tl->len = llen; tl = TAILQ_PREV(tl, bfhead, entries); } TAILQ_REMOVE(&head, first, entries); free(first); } diff --git a/usr.bin/talk/talk.h b/usr.bin/talk/talk.h index be7368ca75d7..83852dfc46b4 100644 --- a/usr.bin/talk/talk.h +++ b/usr.bin/talk/talk.h @@ -1,93 +1,92 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include extern int sockt; extern int curses_initialized; extern int invitation_waiting; extern const char *current_state; extern int current_line; extern volatile sig_atomic_t gotwinch; typedef struct xwin { WINDOW *x_win; int x_nlines; int x_ncols; int x_line; int x_col; char kill; char cerase; char werase; } xwin_t; extern xwin_t my_win; extern xwin_t his_win; extern WINDOW *line_win; extern void announce_invite(void); extern int check_local(void); extern void check_writeable(void); extern void ctl_transact(struct in_addr,CTL_MSG,int,CTL_RESPONSE *); extern void disp_msg(int); extern void end_msgs(void); extern void get_addrs(const char *, const char *); extern int get_iface(struct in_addr *, struct in_addr *); extern void get_names(int, char **); extern void init_display(void); extern void invite_remote(void); extern int look_for_invite(CTL_RESPONSE *); extern int max(int, int); extern void message(const char *); extern void open_ctl(void); extern void open_sockt(void); extern void p_error(const char *); extern void print_addr(struct sockaddr_in); extern void quit(void); extern int readwin(WINDOW *, int, int); extern void re_invite(int); extern void send_delete(void); extern void set_edit_chars(void); extern void sig_sent(int); extern void sig_winch(int); extern void start_msgs(void); extern void talk(void); extern void resize_display(void); diff --git a/usr.bin/tftp/tftp.c b/usr.bin/tftp/tftp.c index a058489b3c22..31a22f416135 100644 --- a/usr.bin/tftp/tftp.c +++ b/usr.bin/tftp/tftp.c @@ -1,271 +1,268 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#if 0 -#endif - #include /* Many bug fixes are from Jim Guyton */ /* * TFTP User Program -- Protocol Machines */ #include #include #include #include #include #include #include #include #include #include #include #include "tftp.h" #include "tftp-file.h" #include "tftp-utils.h" #include "tftp-io.h" #include "tftp-transfer.h" #include "tftp-options.h" /* * Send the requested file. */ int xmitfile(int peer, char *port, int fd, char *name, char *mode) { struct tftphdr *rp; int n, i, ret = 0; uint16_t block; struct sockaddr_storage serv; /* valid server port number */ char recvbuffer[MAXPKTSIZE]; struct tftp_stats tftp_stats; stats_init(&tftp_stats); memset(&serv, 0, sizeof(serv)); rp = (struct tftphdr *)recvbuffer; if (port == NULL) { struct servent *se; se = getservbyname("tftp", "udp"); assert(se != NULL); ((struct sockaddr_in *)&peer_sock)->sin_port = se->s_port; } else ((struct sockaddr_in *)&peer_sock)->sin_port = htons(atoi(port)); for (i = 0; i < 12; i++) { struct sockaddr_storage from; /* Tell the other side what we want to do */ if (debug & DEBUG_SIMPLE) printf("Sending %s\n", name); n = send_wrq(peer, name, mode); if (n > 0) { printf("Cannot send WRQ packet\n"); return -1; } /* * The first packet we receive has the new destination port * we have to send the next packets to. */ n = receive_packet(peer, recvbuffer, MAXPKTSIZE, &from, timeoutpacket); /* We got some data! */ if (n >= 0) { ((struct sockaddr_in *)&peer_sock)->sin_port = ((struct sockaddr_in *)&from)->sin_port; break; } /* This should be retried */ if (n == RP_TIMEOUT) { printf("Try %d, didn't receive answer from remote.\n", i + 1); continue; } /* Everything else is fatal */ break; } if (i == 12) { printf("Transfer timed out.\n"); return -1; } if (rp->th_opcode == ERROR) { printf("Got ERROR, aborted\n"); return -1; } /* * If the first packet is an OACK instead of an ACK packet, * handle it different. */ if (rp->th_opcode == OACK) { if (!options_rfc_enabled) { printf("Got OACK while options are not enabled!\n"); send_error(peer, EBADOP); return -1; } parse_options(peer, rp->th_stuff, n + 2); } if (read_init(fd, NULL, mode) < 0) { warn("read_init()"); return -1; } block = 1; if (tftp_send(peer, &block, &tftp_stats) != 0) ret = -1; read_close(); if (tftp_stats.amount > 0) printstats("Sent", verbose, &tftp_stats); return ret; } /* * Receive a file. */ int recvfile(int peer, char *port, int fd, char *name, char *mode) { struct tftphdr *rp; uint16_t block; char recvbuffer[MAXPKTSIZE]; int n, i, ret = 0; struct tftp_stats tftp_stats; stats_init(&tftp_stats); rp = (struct tftphdr *)recvbuffer; if (port == NULL) { struct servent *se; se = getservbyname("tftp", "udp"); assert(se != NULL); ((struct sockaddr_in *)&peer_sock)->sin_port = se->s_port; } else ((struct sockaddr_in *)&peer_sock)->sin_port = htons(atoi(port)); for (i = 0; i < 12; i++) { struct sockaddr_storage from; /* Tell the other side what we want to do */ if (debug & DEBUG_SIMPLE) printf("Requesting %s\n", name); n = send_rrq(peer, name, mode); if (n > 0) { printf("Cannot send RRQ packet\n"); return -1; } /* * The first packet we receive has the new destination port * we have to send the next packets to. */ n = receive_packet(peer, recvbuffer, MAXPKTSIZE, &from, timeoutpacket); /* We got something useful! */ if (n >= 0) { ((struct sockaddr_in *)&peer_sock)->sin_port = ((struct sockaddr_in *)&from)->sin_port; break; } /* We should retry if this happens */ if (n == RP_TIMEOUT) { printf("Try %d, didn't receive answer from remote.\n", i + 1); continue; } /* Otherwise it is a fatal error */ break; } if (i == 12) { printf("Transfer timed out.\n"); return -1; } if (rp->th_opcode == ERROR) { tftp_log(LOG_ERR, "Error code %d: %s", rp->th_code, rp->th_msg); return -1; } if (write_init(fd, NULL, mode) < 0) { warn("write_init"); return -1; } /* * If the first packet is an OACK packet instead of an DATA packet, * handle it different. */ if (rp->th_opcode == OACK) { if (!options_rfc_enabled) { printf("Got OACK while options are not enabled!\n"); send_error(peer, EBADOP); return -1; } parse_options(peer, rp->th_stuff, n + 2); n = send_ack(peer, 0); if (n > 0) { printf("Cannot send ACK on OACK.\n"); return -1; } block = 0; if (tftp_receive(peer, &block, &tftp_stats, NULL, 0) != 0) ret = -1; } else { block = 1; if (tftp_receive(peer, &block, &tftp_stats, rp, n) != 0) ret = -1; } if (tftp_stats.amount > 0) printstats("Received", verbose, &tftp_stats); return ret; } diff --git a/usr.bin/tip/libacu/biz22.c b/usr.bin/tip/libacu/biz22.c index a2559f99fe85..6ef5b1861049 100644 --- a/usr.bin/tip/libacu/biz22.c +++ b/usr.bin/tip/libacu/biz22.c @@ -1,183 +1,179 @@ /* $OpenBSD: biz22.c,v 1.13 2006/03/17 19:17:13 moritz Exp $ */ /* $NetBSD: biz22.c,v 1.6 1997/02/11 09:24:11 mrg Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - #include "tip.h" #define DISCONNECT_CMD "\20\04" /* disconnection string */ static int dialtimeout = 0; static jmp_buf timeoutbuf; static int biz_dialer(char *, char *); static void sigALRM(int); static int cmd(char *); static int detect(char *); /* * Dial up on a BIZCOMP Model 1022 with either * tone dialing (mod = "V") * pulse dialing (mod = "W") */ static int biz_dialer(char *num, char *mod) { int connected = 0; char cbuf[40]; if (boolean(value(VERBOSE))) printf("\nstarting call..."); /* * Disable auto-answer and configure for tone/pulse * dialing */ if (cmd("\02K\r")) { printf("can't initialize bizcomp..."); return (0); } (void)strlcpy(cbuf, "\02.\r", sizeof cbuf); cbuf[1] = *mod; if (cmd(cbuf)) { printf("can't set dialing mode..."); return (0); } (void)snprintf(cbuf, sizeof(cbuf), "\02D%s\r", num); write(FD, cbuf, strlen(cbuf)); if (!detect("7\r")) { printf("can't get dial tone..."); return (0); } if (boolean(value(VERBOSE))) printf("ringing..."); /* * The reply from the BIZCOMP should be: * 2 \r or 7 \r failure * 1 \r success */ connected = detect("1\r"); #ifdef ACULOG if (dialtimeout) { char line[80]; (void)snprintf(line, sizeof line, "%ld second dial timeout", number(value(DIALTIMEOUT))); logent(value(HOST), num, "biz1022", line); } #endif if (dialtimeout) biz22_disconnect(); /* insurance */ return (connected); } int biz22w_dialer(char *num, char *acu) { return (biz_dialer(num, "W")); } int biz22f_dialer(char *num, char *acu) { return (biz_dialer(num, "V")); } void biz22_disconnect(void) { write(FD, DISCONNECT_CMD, sizeof(DISCONNECT_CMD)-1); sleep(2); tcflush(FD, TCIOFLUSH); } void biz22_abort(void) { write(FD, "\02", 1); } /*ARGSUSED*/ static void sigALRM(int signo) { dialtimeout = 1; longjmp(timeoutbuf, 1); } static int cmd(char *s) { sig_t f; char c; write(FD, s, strlen(s)); f = signal(SIGALRM, sigALRM); if (setjmp(timeoutbuf)) { biz22_abort(); signal(SIGALRM, f); return (1); } alarm(number(value(DIALTIMEOUT))); read(FD, &c, 1); alarm(0); signal(SIGALRM, f); c &= 0177; return (c != '\r'); } static int detect(char *s) { sig_t f; char c; f = signal(SIGALRM, sigALRM); dialtimeout = 0; while (*s) { if (setjmp(timeoutbuf)) { biz22_abort(); break; } alarm(number(value(DIALTIMEOUT))); read(FD, &c, 1); alarm(0); c &= 0177; if (c != *s++) return (0); } signal(SIGALRM, f); return (dialtimeout == 0); } diff --git a/usr.bin/tip/libacu/biz31.c b/usr.bin/tip/libacu/biz31.c index 26d3287c1266..cd2cc0267b43 100644 --- a/usr.bin/tip/libacu/biz31.c +++ b/usr.bin/tip/libacu/biz31.c @@ -1,250 +1,246 @@ /* $OpenBSD: biz31.c,v 1.10 2006/03/17 19:17:13 moritz Exp $ */ /* $NetBSD: biz31.c,v 1.5 1997/02/11 09:24:14 mrg Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - #include "tip.h" #define MAXRETRY 3 /* sync up retry count */ #define DISCONNECT_CMD "\21\25\11\24" /* disconnection string */ static int biz_dialer(char *, char *); static int bizsync(int); static int echo(char *); static void sigALRM(int); static int detect(char *); static int flush(char *); static int bizsync(int); static int timeout = 0; static jmp_buf timeoutbuf; /* * Dial up on a BIZCOMP Model 1031 with either * tone dialing (mod = "f") * pulse dialing (mod = "w") */ static int biz_dialer(char *num, char *mod) { int connected = 0; if (!bizsync(FD)) { logent(value(HOST), "", "biz", "out of sync"); printf("bizcomp out of sync\n"); delock(uucplock); exit(0); } if (boolean(value(VERBOSE))) printf("\nstarting call..."); echo("#\rk$\r$\n"); /* disable auto-answer */ echo("$>$.$ #\r"); /* tone/pulse dialing */ echo(mod); echo("$\r$\n"); echo("$>$.$ #\re$ "); /* disconnection sequence */ echo(DISCONNECT_CMD); echo("\r$\n$\r$\n"); echo("$>$.$ #\rr$ "); /* repeat dial */ echo(num); echo("\r$\n"); if (boolean(value(VERBOSE))) printf("ringing..."); /* * The reply from the BIZCOMP should be: * `^G NO CONNECTION\r\n^G\r\n' failure * ` CONNECTION\r\n^G' success */ connected = detect(" "); #ifdef ACULOG if (timeout) { char line[80]; (void)snprintf(line, sizeof line, "%ld second dial timeout", number(value(DIALTIMEOUT))); logent(value(HOST), num, "biz", line); } #endif if (!connected) flush(" NO CONNECTION\r\n\07\r\n"); else flush("CONNECTION\r\n\07"); if (timeout) biz31_disconnect(); /* insurance */ return (connected); } int biz31w_dialer(char *num, char *acu) { return (biz_dialer(num, "w")); } int biz31f_dialer(char *num, char *acu) { return (biz_dialer(num, "f")); } void biz31_disconnect(void) { write(FD, DISCONNECT_CMD, sizeof(DISCONNECT_CMD)-1); sleep(2); tcflush(FD, TCIOFLUSH); } void biz31_abort(void) { write(FD, "\33", 1); } static int echo(char *s) { char c; while (c = *s++) switch (c) { case '$': read(FD, &c, 1); s++; break; case '#': c = *s++; write(FD, &c, 1); break; default: write(FD, &c, 1); read(FD, &c, 1); } } /*ARGSUSED*/ static void sigALRM(int signo) { timeout = 1; longjmp(timeoutbuf, 1); } static int detect(char *s) { sig_t f; char c; f = signal(SIGALRM, sigALRM); timeout = 0; while (*s) { if (setjmp(timeoutbuf)) { printf("\07timeout waiting for reply\n"); biz31_abort(); break; } alarm(number(value(DIALTIMEOUT))); read(FD, &c, 1); alarm(0); if (c != *s++) break; } signal(SIGALRM, f); return (timeout == 0); } static int flush(char *s) { sig_t f; char c; f = signal(SIGALRM, sigALRM); while (*s++) { if (setjmp(timeoutbuf)) break; alarm(10); read(FD, &c, 1); alarm(0); } signal(SIGALRM, f); timeout = 0; /* guard against disconnection */ } /* * This convoluted piece of code attempts to get * the bizcomp in sync. If you don't have the capacity or nread * call there are gory ways to simulate this. */ static int bizsync(int fd) { #ifdef FIOCAPACITY struct capacity b; # define chars(b) ((b).cp_nbytes) # define IOCTL FIOCAPACITY #endif #ifdef FIONREAD long b; # define chars(b) (b) # define IOCTL FIONREAD #endif int already = 0; char buf[10]; retry: if (ioctl(fd, IOCTL, (caddr_t)&b) >= 0 && chars(b) > 0) tcflush(FD, TCIOFLUSH); write(fd, "\rp>\r", 4); sleep(1); if (ioctl(fd, IOCTL, (caddr_t)&b) >= 0) { if (chars(b) != 10) { nono: if (already > MAXRETRY) return (0); write(fd, DISCONNECT_CMD, 4); sleep(2); already++; goto retry; } else { read(fd, buf, 10); if (strncmp(buf, "p >\r\n\r\n>", 8)) goto nono; } } return (1); } diff --git a/usr.bin/tip/libacu/courier.c b/usr.bin/tip/libacu/courier.c index e0b4af9304bf..7ff0daa9d1db 100644 --- a/usr.bin/tip/libacu/courier.c +++ b/usr.bin/tip/libacu/courier.c @@ -1,350 +1,346 @@ /* $OpenBSD: courier.c,v 1.15 2006/03/17 19:17:13 moritz Exp $ */ /* $NetBSD: courier.c,v 1.7 1997/02/11 09:24:16 mrg Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - /* * Routines for calling up on a Courier modem. * Derived from Hayes driver. */ #include "tip.h" #include #include #define MAXRETRY 5 static int dialtimeout = 0; static int connected = 0; static jmp_buf timeoutbuf; static void sigALRM(int); static int cour_swallow(char *); static int cour_connect(void); static int coursync(void); static void cour_write(int, char *, int); static void cour_nap(void); #ifdef DEBUG static void cour_verbose_read(void); #endif int cour_dialer(char *num, char *acu) { char *cp; #ifdef ACULOG char line[80]; #endif struct termios cntrl; if (boolean(value(VERBOSE))) printf("Using \"%s\"\n", acu); tcgetattr(FD, &cntrl); cntrl.c_cflag |= HUPCL; tcsetattr(FD, TCSAFLUSH, &cntrl); /* * Get in synch. */ if (!coursync()) { badsynch: printf("can't synchronize with courier\n"); #ifdef ACULOG logent(value(HOST), num, "courier", "can't synch up"); #endif return (0); } cour_write(FD, "AT E0\r", 6); /* turn off echoing */ sleep(1); #ifdef DEBUG if (boolean(value(VERBOSE))) cour_verbose_read(); #endif tcflush(FD, TCIOFLUSH); cour_write(FD, "AT C1 E0 H0 Q0 X6 V1\r", 21); if (!cour_swallow("\r\nOK\r\n")) goto badsynch; fflush(stdout); cour_write(FD, "AT D", 4); for (cp = num; *cp; cp++) if (*cp == '=') *cp = ','; cour_write(FD, num, strlen(num)); cour_write(FD, "\r", 1); connected = cour_connect(); #ifdef ACULOG if (dialtimeout) { (void)snprintf(line, sizeof line, "%ld second dial timeout", number(value(DIALTIMEOUT))); logent(value(HOST), num, "cour", line); } #endif if (dialtimeout) cour_disconnect(); return (connected); } void cour_disconnect(void) { /* first hang up the modem*/ ioctl(FD, TIOCCDTR, 0); sleep(1); ioctl(FD, TIOCSDTR, 0); coursync(); /* reset */ close(FD); } void cour_abort(void) { cour_write(FD, "\r", 1); /* send anything to abort the call */ cour_disconnect(); } /*ARGSUSED*/ static void sigALRM(int signo) { printf("\07timeout waiting for reply\n"); dialtimeout = 1; longjmp(timeoutbuf, 1); } static int cour_swallow(char *match) { sig_t f; char c; f = signal(SIGALRM, sigALRM); dialtimeout = 0; do { if (*match =='\0') { signal(SIGALRM, f); return (1); } if (setjmp(timeoutbuf)) { signal(SIGALRM, f); return (0); } alarm(number(value(DIALTIMEOUT))); read(FD, &c, 1); alarm(0); c &= 0177; #ifdef DEBUG if (boolean(value(VERBOSE))) putchar(c); #endif } while (c == *match++); #ifdef DEBUG if (boolean(value(VERBOSE))) fflush(stdout); #endif signal(SIGALRM, SIG_DFL); return (0); } struct baud_msg { char *msg; int baud; } baud_msg[] = { { "", B300 }, { " 1200", B1200 }, { " 2400", B2400 }, { " 9600", B9600 }, { " 9600/ARQ", B9600 }, { 0, 0 }, }; static int cour_connect(void) { char c; int nc, nl, n; char dialer_buf[64]; struct baud_msg *bm; sig_t f; if (cour_swallow("\r\n") == 0) return (0); f = signal(SIGALRM, sigALRM); again: nc = 0; nl = sizeof(dialer_buf)-1; bzero(dialer_buf, sizeof(dialer_buf)); dialtimeout = 0; for (nc = 0, nl = sizeof(dialer_buf)-1 ; nl > 0 ; nc++, nl--) { if (setjmp(timeoutbuf)) break; alarm(number(value(DIALTIMEOUT))); n = read(FD, &c, 1); alarm(0); if (n <= 0) break; c &= 0x7f; if (c == '\r') { if (cour_swallow("\n") == 0) break; if (!dialer_buf[0]) goto again; if (strcmp(dialer_buf, "RINGING") == 0 && boolean(value(VERBOSE))) { #ifdef DEBUG printf("%s\r\n", dialer_buf); #endif goto again; } if (strncmp(dialer_buf, "CONNECT", sizeof("CONNECT")-1) != 0) break; for (bm = baud_msg ; bm->msg ; bm++) if (strcmp(bm->msg, dialer_buf+sizeof("CONNECT")-1) == 0) { struct termios cntrl; tcgetattr(FD, &cntrl); cfsetospeed(&cntrl, bm->baud); cfsetispeed(&cntrl, bm->baud); tcsetattr(FD, TCSAFLUSH, &cntrl); signal(SIGALRM, f); #ifdef DEBUG if (boolean(value(VERBOSE))) printf("%s\r\n", dialer_buf); #endif return (1); } break; } dialer_buf[nc] = c; #ifdef notdef if (boolean(value(VERBOSE))) putchar(c); #endif } printf("%s\r\n", dialer_buf); signal(SIGALRM, f); return (0); } /* * This convoluted piece of code attempts to get * the courier in sync. */ static int coursync(void) { int already = 0; int len; char buf[40]; while (already++ < MAXRETRY) { tcflush(FD, TCIOFLUSH); cour_write(FD, "\rAT Z\r", 6); /* reset modem */ bzero(buf, sizeof(buf)); sleep(1); ioctl(FD, FIONREAD, &len); if (len) { len = read(FD, buf, sizeof(buf)); #ifdef DEBUG buf[len] = '\0'; printf("coursync: (\"%s\")\n\r", buf); #endif if (strchr(buf, '0') || (strchr(buf, 'O') && strchr(buf, 'K'))) return(1); } /* * If not strapped for DTR control, * try to get command mode. */ sleep(1); cour_write(FD, "+++", 3); sleep(1); /* * Toggle DTR to force anyone off that might have left * the modem connected. */ ioctl(FD, TIOCCDTR, 0); sleep(1); ioctl(FD, TIOCSDTR, 0); } cour_write(FD, "\rAT Z\r", 6); return (0); } static void cour_write(int fd, char *cp, int n) { #ifdef notdef if (boolean(value(VERBOSE))) write(1, cp, n); #endif tcdrain(fd); cour_nap(); for ( ; n-- ; cp++) { write(fd, cp, 1); tcdrain(fd); cour_nap(); } } #ifdef DEBUG static void cour_verbose_read(void) { int n = 0; char buf[BUFSIZ]; if (ioctl(FD, FIONREAD, &n) < 0) return; if (n <= 0) return; if (read(FD, buf, n) != n) return; write(1, buf, n); } #endif /* Give the courier 50 milliseconds between characters */ static void cour_nap(void) { struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = 50 * 1000000; nanosleep(&ts, NULL); } diff --git a/usr.bin/tip/libacu/df.c b/usr.bin/tip/libacu/df.c index bae618fc9671..5b9cf7186a69 100644 --- a/usr.bin/tip/libacu/df.c +++ b/usr.bin/tip/libacu/df.c @@ -1,133 +1,129 @@ /* $OpenBSD: df.c,v 1.9 2006/03/17 19:17:13 moritz Exp $ */ /* $NetBSD: df.c,v 1.4 1995/10/29 00:49:51 pk Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - /* * Dial the DF02-AC or DF03-AC */ #include "tip.h" static jmp_buf Sjbuf; static int df_dialer(char *, char *, int); static void alrm_timeout(int); int df02_dialer(char *num, char *acu) { return (df_dialer(num, acu, 0)); } int df03_dialer(char *num, char *acu) { return (df_dialer(num, acu, 1)); } static int df_dialer(char *num, char *acu, int df03) { int f = FD; struct termios cntrl; int speed = 0; char c = '\0'; tcgetattr(f, &cntrl); cntrl.c_cflag |= HUPCL; tcsetattr(f, TCSANOW, &cntrl); if (setjmp(Sjbuf)) { printf("connection timed out\r\n"); df_disconnect(); return (0); } if (boolean(value(VERBOSE))) printf("\ndialing..."); fflush(stdout); #ifdef TIOCMSET if (df03) { int st = TIOCM_ST; /* secondary Transmit flag */ tcgetattr(f, &cntrl); speed = cfgetospeed(&cntrl); if (speed != B1200) { /* must dial at 1200 baud */ cfsetospeed(&cntrl, B1200); cfsetispeed(&cntrl, B1200); tcsetattr(f, TCSAFLUSH, &cntrl); ioctl(f, TIOCMBIC, &st); /* clear ST for 300 baud */ } else ioctl(f, TIOCMBIS, &st); /* set ST for 1200 baud */ } #endif signal(SIGALRM, alrm_timeout); alarm(5 * strlen(num) + 10); tcflush(f, TCIOFLUSH); write(f, "\001", 1); sleep(1); write(f, "\002", 1); write(f, num, strlen(num)); read(f, &c, 1); #ifdef TIOCMSET if (df03 && speed != B1200) { cfsetospeed(&cntrl, speed); cfsetispeed(&cntrl, speed); tcsetattr(f, TCSAFLUSH, &cntrl); } #endif return (c == 'A'); } void df_disconnect(void) { write(FD, "\001", 1); sleep(1); tcflush(FD, TCIOFLUSH); } void df_abort(void) { df_disconnect(); } /*ARGSUSED*/ static void alrm_timeout(int signo) { longjmp(Sjbuf, 1); } diff --git a/usr.bin/tip/libacu/dn11.c b/usr.bin/tip/libacu/dn11.c index 706a9df25352..8f9f47065d69 100644 --- a/usr.bin/tip/libacu/dn11.c +++ b/usr.bin/tip/libacu/dn11.c @@ -1,145 +1,141 @@ /* $OpenBSD: dn11.c,v 1.9 2006/03/17 19:17:13 moritz Exp $ */ /* $NetBSD: dn11.c,v 1.4 1995/10/29 00:49:53 pk Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - /* * Routines for dialing up on DN-11 */ #include "tip.h" static jmp_buf jmpbuf; static pid_t child = -1, dn; static void alarmtr(int); int dn_dialer(char *num, char *acu) { int lt, nw; int timelim; struct termios cntrl; if (boolean(value(VERBOSE))) printf("\nstarting call..."); if ((dn = open(acu, 1)) < 0) { if (errno == EBUSY) printf("line busy..."); else printf("acu open error..."); return (0); } if (setjmp(jmpbuf)) { kill(child, SIGKILL); close(dn); return (0); } signal(SIGALRM, alarmtr); timelim = 5 * strlen(num); alarm(timelim < 30 ? 30 : timelim); if ((child = fork()) == 0) { /* * ignore this stuff for aborts */ signal(SIGALRM, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); sleep(2); nw = write(dn, num, lt = strlen(num)); exit(nw != lt); } /* * open line - will return on carrier */ if ((FD = open(DV, 2)) < 0) { if (errno == EIO) printf("lost carrier..."); else printf("dialup line open failed..."); alarm(0); kill(child, SIGKILL); close(dn); return (0); } alarm(0); tcgetattr(dn, &cntrl); cntrl.c_cflag |= HUPCL; tcsetattr(dn, TCSANOW, &cntrl); signal(SIGALRM, SIG_DFL); while ((nw = wait(<)) != child && nw != -1) ; fflush(stdout); close(dn); if (lt != 0) { close(FD); return (0); } return (1); } /*ARGSUSED*/ static void alarmtr(int signo) { alarm(0); longjmp(jmpbuf, 1); } /* * Insurance, for some reason we don't seem to be * hanging up... */ void dn_disconnect(void) { sleep(2); if (FD > 0) ioctl(FD, TIOCCDTR, 0); close(FD); } void dn_abort(void) { sleep(2); if (child > 0) kill(child, SIGKILL); if (dn > 0) close(dn); if (FD > 0) ioctl(FD, TIOCCDTR, 0); close(FD); } diff --git a/usr.bin/tip/libacu/hayes.c b/usr.bin/tip/libacu/hayes.c index cd4d3689df24..efdd7fc8b438 100644 --- a/usr.bin/tip/libacu/hayes.c +++ b/usr.bin/tip/libacu/hayes.c @@ -1,316 +1,312 @@ /* $OpenBSD: hayes.c,v 1.13 2006/03/17 19:17:13 moritz Exp $ */ /* $NetBSD: hayes.c,v 1.6 1997/02/11 09:24:17 mrg Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - /* * Routines for calling up on a Hayes Modem * (based on the old VenTel driver). * The modem is expected to be strapped for "echo". * Also, the switches enabling the DTR and CD lines * must be set correctly. * NOTICE: * The easy way to hang up a modem is always simply to * clear the DTR signal. However, if the +++ sequence * (which switches the modem back to local mode) is sent * before modem is hung up, removal of the DTR signal * has no effect (except that it prevents the modem from * recognizing commands). * (by Helge Skrivervik, Calma Company, Sunnyvale, CA. 1984) */ /* * TODO: * It is probably not a good idea to switch the modem * state between 'verbose' and terse (status messages). * This should be kicked out and we should use verbose * mode only. This would make it consistent with normal * interactive use thru the command 'tip dialer'. */ #include "tip.h" #include #include #define min(a,b) ((a < b) ? a : b) static int dialtimeout = 0; static jmp_buf timeoutbuf; #define DUMBUFLEN 40 static char dumbuf[DUMBUFLEN]; #define DIALING 1 #define IDLE 2 #define CONNECTED 3 #define FAILED 4 static int state = IDLE; static void sigALRM(int); static char gobble(char *); static void error_rep(char); static void goodbye(void); static int hay_sync(void); int hay_dialer(char *num, char *acu) { char *cp; int connected = 0; char dummy; struct termios cntrl; #ifdef ACULOG char line[80]; #endif if (hay_sync() == 0) /* make sure we can talk to the modem */ return(0); if (boolean(value(VERBOSE))) printf("\ndialing..."); fflush(stdout); tcgetattr(FD, &cntrl); cntrl.c_cflag |= HUPCL; tcsetattr(FD, TCSANOW, &cntrl); tcflush(FD, TCIOFLUSH); write(FD, "ATv0\r", 5); /* tell modem to use short status codes */ gobble("\r"); gobble("\r"); write(FD, "ATTD", 4); /* send dial command */ for (cp = num; *cp; cp++) if (*cp == '=') *cp = ','; write(FD, num, strlen(num)); state = DIALING; write(FD, "\r", 1); connected = 0; if (gobble("\r")) { if ((dummy = gobble("01234")) != '1') error_rep(dummy); else connected = 1; } if (connected) state = CONNECTED; else { state = FAILED; return (connected); /* lets get out of here.. */ } tcflush(FD, TCIOFLUSH); #ifdef ACULOG if (dialtimeout) { (void)snprintf(line, sizeof line, "%ld second dial timeout", number(value(DIALTIMEOUT))); logent(value(HOST), num, "hayes", line); } #endif if (dialtimeout) hay_disconnect(); /* insurance */ return (connected); } void hay_disconnect(void) { /* first hang up the modem*/ #ifdef DEBUG printf("\rdisconnecting modem....\n\r"); #endif ioctl(FD, TIOCCDTR, 0); sleep(1); ioctl(FD, TIOCSDTR, 0); goodbye(); } void hay_abort(void) { write(FD, "\r", 1); /* send anything to abort the call */ hay_disconnect(); } /*ARGSUSED*/ static void sigALRM(int signo) { printf("\07timeout waiting for reply\n\r"); dialtimeout = 1; longjmp(timeoutbuf, 1); } static char gobble(char *match) { char c; sig_t f; size_t i; int status = 0; f = signal(SIGALRM, sigALRM); dialtimeout = 0; #ifdef DEBUG printf("\ngobble: waiting for %s\n", match); #endif do { if (setjmp(timeoutbuf)) { signal(SIGALRM, f); return (0); } alarm(number(value(DIALTIMEOUT))); read(FD, &c, 1); alarm(0); c &= 0177; #ifdef DEBUG printf("%c 0x%x ", c, c); #endif for (i = 0; i < strlen(match); i++) if (c == match[i]) status = c; } while (status == 0); signal(SIGALRM, SIG_DFL); #ifdef DEBUG printf("\n"); #endif return (status); } static void error_rep(char c) { printf("\n\r"); switch (c) { case '0': printf("OK"); break; case '1': printf("CONNECT"); break; case '2': printf("RING"); break; case '3': printf("NO CARRIER"); break; case '4': printf("ERROR in input"); break; case '5': printf("CONNECT 1200"); break; default: printf("Unknown Modem error: %c (0x%x)", c, c); } printf("\n\r"); return; } /* * set modem back to normal verbose status codes. */ static void goodbye(void) { int len; char c; tcflush(FD, TCIOFLUSH); if (hay_sync()) { sleep(1); #ifndef DEBUG tcflush(FD, TCIOFLUSH); #endif write(FD, "ATH0\r", 5); /* insurance */ #ifndef DEBUG c = gobble("03"); if (c != '0' && c != '3') { printf("cannot hang up modem\n\r"); printf("please use 'tip dialer' to make sure the line is hung up\n\r"); } #endif sleep(1); ioctl(FD, FIONREAD, &len); #ifdef DEBUG printf("goodbye1: len=%d -- ", len); rlen = read(FD, dumbuf, min(len, DUMBUFLEN)); dumbuf[rlen] = '\0'; printf("read (%d): %s\r\n", rlen, dumbuf); #endif write(FD, "ATv1\r", 5); sleep(1); #ifdef DEBUG ioctl(FD, FIONREAD, &len); printf("goodbye2: len=%d -- ", len); rlen = read(FD, dumbuf, min(len, DUMBUFLEN)); dumbuf[rlen] = '\0'; printf("read (%d): %s\r\n", rlen, dumbuf); #endif } tcflush(FD, TCIOFLUSH); ioctl(FD, TIOCCDTR, 0); /* clear DTR (insurance) */ close(FD); } #define MAXRETRY 5 static int hay_sync(void) { int len, retry = 0; while (retry++ <= MAXRETRY) { write(FD, "AT\r", 3); sleep(1); ioctl(FD, FIONREAD, &len); if (len) { len = read(FD, dumbuf, min(len, DUMBUFLEN)); if (strchr(dumbuf, '0') || (strchr(dumbuf, 'O') && strchr(dumbuf, 'K'))) return(1); #ifdef DEBUG dumbuf[len] = '\0'; printf("hay_sync: (\"%s\") %d\n\r", dumbuf, retry); #endif } ioctl(FD, TIOCCDTR, 0); ioctl(FD, TIOCSDTR, 0); } printf("Cannot synchronize with hayes...\n\r"); return(0); } diff --git a/usr.bin/tip/libacu/t3000.c b/usr.bin/tip/libacu/t3000.c index 7b7288872b8a..9341430787da 100644 --- a/usr.bin/tip/libacu/t3000.c +++ b/usr.bin/tip/libacu/t3000.c @@ -1,368 +1,364 @@ /* $OpenBSD: t3000.c,v 1.14 2006/03/17 19:17:13 moritz Exp $ */ /* $NetBSD: t3000.c,v 1.5 1997/02/11 09:24:18 mrg Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - /* * Routines for calling up on a Telebit T3000 modem. * Derived from Courier driver. */ #include "tip.h" #include #include #define MAXRETRY 5 static int dialtimeout = 0; static int connected = 0; static jmp_buf timeoutbuf; static void sigALRM(int); static int t3000_swallow(char *); static int t3000_connect(void); static int t3000_sync(void); static void t3000_write(int, char *, int); static void t3000_nap(void); #ifdef DEBUG static void t3000_verbose_read(void); #endif int t3000_dialer(char *num, char *acu) { char *cp; struct termios cntrl; #ifdef ACULOG char line[80]; #endif if (boolean(value(VERBOSE))) printf("Using \"%s\"\n", acu); tcgetattr(FD, &cntrl); cntrl.c_cflag |= HUPCL; tcsetattr(FD, TCSANOW, &cntrl); /* * Get in synch. */ if (!t3000_sync()) { badsynch: printf("can't synchronize with t3000\n"); #ifdef ACULOG logent(value(HOST), num, "t3000", "can't synch up"); #endif return (0); } t3000_write(FD, "AT E0\r", 6); /* turn off echoing */ sleep(1); #ifdef DEBUG if (boolean(value(VERBOSE))) t3000_verbose_read(); #endif tcflush(FD, TCIOFLUSH); t3000_write(FD, "AT E0 H0 Q0 X4 V1\r", 18); if (!t3000_swallow("\r\nOK\r\n")) goto badsynch; fflush(stdout); t3000_write(FD, "AT D", 4); for (cp = num; *cp; cp++) if (*cp == '=') *cp = ','; t3000_write(FD, num, strlen(num)); t3000_write(FD, "\r", 1); connected = t3000_connect(); #ifdef ACULOG if (dialtimeout) { (void)snprintf(line, sizeof line, "%ld second dial timeout", number(value(DIALTIMEOUT))); logent(value(HOST), num, "t3000", line); } #endif if (dialtimeout) t3000_disconnect(); return (connected); } void t3000_disconnect(void) { /* first hang up the modem*/ ioctl(FD, TIOCCDTR, 0); sleep(1); ioctl(FD, TIOCSDTR, 0); t3000_sync(); /* reset */ close(FD); } void t3000_abort(void) { t3000_write(FD, "\r", 1); /* send anything to abort the call */ t3000_disconnect(); } /*ARGSUSED*/ static void sigALRM(int signo) { printf("\07timeout waiting for reply\n"); dialtimeout = 1; longjmp(timeoutbuf, 1); } static int t3000_swallow(char *match) { sig_t f; char c; f = signal(SIGALRM, sigALRM); dialtimeout = 0; do { if (*match =='\0') { signal(SIGALRM, f); return (1); } if (setjmp(timeoutbuf)) { signal(SIGALRM, f); return (0); } alarm(number(value(DIALTIMEOUT))); read(FD, &c, 1); alarm(0); c &= 0177; #ifdef DEBUG if (boolean(value(VERBOSE))) putchar(c); #endif } while (c == *match++); #ifdef DEBUG if (boolean(value(VERBOSE))) fflush(stdout); #endif signal(SIGALRM, SIG_DFL); return (0); } #ifndef B19200 /* XXX */ #define B19200 EXTA #define B38400 EXTB #endif struct tbaud_msg { char *msg; int baud; int baud2; } tbaud_msg[] = { { "", B300, 0 }, { " 1200", B1200, 0 }, { " 2400", B2400, 0 }, { " 4800", B4800, 0 }, { " 9600", B9600, 0 }, { " 14400", B19200, B9600 }, { " 19200", B19200, B9600 }, { " 38400", B38400, B9600 }, { " 57600", B38400, B9600 }, { " 7512", B9600, 0 }, { " 1275", B2400, 0 }, { " 7200", B9600, 0 }, { " 12000", B19200, B9600 }, { 0, 0, 0 }, }; static int t3000_connect(void) { char c; int nc, nl, n; char dialer_buf[64]; struct tbaud_msg *bm; sig_t f; if (t3000_swallow("\r\n") == 0) return (0); f = signal(SIGALRM, sigALRM); again: nc = 0; nl = sizeof(dialer_buf)-1; bzero(dialer_buf, sizeof(dialer_buf)); dialtimeout = 0; for (nc = 0, nl = sizeof(dialer_buf)-1 ; nl > 0 ; nc++, nl--) { if (setjmp(timeoutbuf)) break; alarm(number(value(DIALTIMEOUT))); n = read(FD, &c, 1); alarm(0); if (n <= 0) break; c &= 0x7f; if (c == '\r') { if (t3000_swallow("\n") == 0) break; if (!dialer_buf[0]) goto again; if (strcmp(dialer_buf, "RINGING") == 0 && boolean(value(VERBOSE))) { #ifdef DEBUG printf("%s\r\n", dialer_buf); #endif goto again; } if (strncmp(dialer_buf, "CONNECT", sizeof("CONNECT")-1) != 0) break; for (bm = tbaud_msg ; bm->msg ; bm++) if (strcmp(bm->msg, dialer_buf+sizeof("CONNECT")-1) == 0) { struct termios cntrl; tcgetattr(FD, &cntrl); cfsetospeed(&cntrl, bm->baud); cfsetispeed(&cntrl, bm->baud); tcsetattr(FD, TCSAFLUSH, &cntrl); signal(SIGALRM, f); #ifdef DEBUG if (boolean(value(VERBOSE))) printf("%s\r\n", dialer_buf); #endif return (1); } break; } dialer_buf[nc] = c; #ifdef notdef if (boolean(value(VERBOSE))) putchar(c); #endif } printf("%s\r\n", dialer_buf); signal(SIGALRM, f); return (0); } /* * This convoluted piece of code attempts to get * the t3000 in sync. */ static int t3000_sync(void) { int already = 0; int len; char buf[40]; while (already++ < MAXRETRY) { tcflush(FD, TCIOFLUSH); t3000_write(FD, "\rAT Z\r", 6); /* reset modem */ bzero(buf, sizeof(buf)); sleep(2); ioctl(FD, FIONREAD, &len); #if 1 if (len == 0) len = 1; #endif if (len) { len = read(FD, buf, sizeof(buf)); #ifdef DEBUG buf[len] = '\0'; printf("t3000_sync: (\"%s\")\n\r", buf); #endif if (strchr(buf, '0') || (strchr(buf, 'O') && strchr(buf, 'K'))) return(1); } /* * If not strapped for DTR control, * try to get command mode. */ sleep(1); t3000_write(FD, "+++", 3); sleep(1); /* * Toggle DTR to force anyone off that might have left * the modem connected. */ ioctl(FD, TIOCCDTR, 0); sleep(1); ioctl(FD, TIOCSDTR, 0); } t3000_write(FD, "\rAT Z\r", 6); return (0); } static void t3000_write(int fd, char *cp, int n) { #ifdef notdef if (boolean(value(VERBOSE))) write(1, cp, n); #endif tcdrain(fd); t3000_nap(); for ( ; n-- ; cp++) { write(fd, cp, 1); tcdrain(fd); t3000_nap(); } } #ifdef DEBUG static void t3000_verbose_read(void) { int n = 0; char buf[BUFSIZ]; if (ioctl(FD, FIONREAD, &n) < 0) return; if (n <= 0) return; if (read(FD, buf, n) != n) return; write(1, buf, n); } #endif /* Give the t3000 50 milliseconds between characters */ static void t3000_nap(void) { struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = 50 * 1000000; nanosleep(&ts, NULL); } diff --git a/usr.bin/tip/libacu/v3451.c b/usr.bin/tip/libacu/v3451.c index 8d360043f9e6..1f671b5bffd3 100644 --- a/usr.bin/tip/libacu/v3451.c +++ b/usr.bin/tip/libacu/v3451.c @@ -1,208 +1,204 @@ /* $OpenBSD: v3451.c,v 1.9 2006/03/17 19:17:13 moritz Exp $ */ /* $NetBSD: v3451.c,v 1.6 1997/02/11 09:24:20 mrg Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - /* * Routines for calling up on a Vadic 3451 Modem */ #include "tip.h" static jmp_buf Sjbuf; static void vawrite(char *, int); static int expect(char *); static void alarmtr(int); static int notin(char *, char *); static int prefix(char *, char *); int v3451_dialer(char *num, char *acu) { sig_t func; int ok; int slow = number(value(BAUDRATE)) < 1200; char phone[50]; struct termios cntrl; /* * Get in synch */ vawrite("I\r", 1 + slow); vawrite("I\r", 1 + slow); vawrite("I\r", 1 + slow); vawrite("\005\r", 2 + slow); if (!expect("READY")) { printf("can't synchronize with vadic 3451\n"); #ifdef ACULOG logent(value(HOST), num, "vadic", "can't synch up"); #endif return (0); } tcgetattr(FD, &cntrl); term.c_cflag |= HUPCL; tcsetattr(FD, TCSANOW, &cntrl); sleep(1); vawrite("D\r", 2 + slow); if (!expect("NUMBER?")) { printf("Vadic will not accept dial command\n"); #ifdef ACULOG logent(value(HOST), num, "vadic", "will not accept dial"); #endif return (0); } (void)snprintf(phone, sizeof phone, "%s\r", num); vawrite(phone, 1 + slow); if (!expect(phone)) { printf("Vadic will not accept phone number\n"); #ifdef ACULOG logent(value(HOST), num, "vadic", "will not accept number"); #endif return (0); } func = signal(SIGINT,SIG_IGN); /* * You cannot interrupt the Vadic when its dialing; * even dropping DTR does not work (definitely a * brain damaged design). */ vawrite("\r", 1 + slow); vawrite("\r", 1 + slow); if (!expect("DIALING:")) { printf("Vadic failed to dial\n"); #ifdef ACULOG logent(value(HOST), num, "vadic", "failed to dial"); #endif return (0); } if (boolean(value(VERBOSE))) printf("\ndialing..."); ok = expect("ON LINE"); signal(SIGINT, func); if (!ok) { printf("call failed\n"); #ifdef ACULOG logent(value(HOST), num, "vadic", "call failed"); #endif return (0); } tcflush(FD, TCIOFLUSH); return (1); } void v3451_disconnect(void) { close(FD); } void v3451_abort(void) { close(FD); } static void vawrite(char *cp, int delay) { for (; *cp; sleep(delay), cp++) write(FD, cp, 1); } static int expect(char *cp) { char buf[300]; char *rp = buf; int timeout = 30, online = 0; if (strcmp(cp, "\"\"") == 0) return (1); *rp = 0; /* * If we are waiting for the Vadic to complete * dialing and get a connection, allow more time * Unfortunately, the Vadic times out 24 seconds after * the last digit is dialed */ online = strcmp(cp, "ON LINE") == 0; if (online) timeout = number(value(DIALTIMEOUT)); signal(SIGALRM, alarmtr); if (setjmp(Sjbuf)) return (0); alarm(timeout); while (notin(cp, buf) && rp < buf + sizeof (buf) - 1) { if (online && notin("FAILED CALL", buf) == 0) return (0); if (read(FD, rp, 1) < 0) { alarm(0); return (0); } if (*rp &= 0177) rp++; *rp = '\0'; } alarm(0); return (1); } /*ARGSUSED*/ static void alarmtr(int signo) { longjmp(Sjbuf, 1); } static int notin(char *sh, char *lg) { for (; *lg; lg++) if (prefix(sh, lg)) return (0); return (1); } static int prefix(char *s1, char *s2) { char c; while ((c = *s1++) == *s2++) if (c == '\0') return (1); return (c == '\0'); } diff --git a/usr.bin/tip/libacu/v831.c b/usr.bin/tip/libacu/v831.c index 41410a8108b4..b233cf55c280 100644 --- a/usr.bin/tip/libacu/v831.c +++ b/usr.bin/tip/libacu/v831.c @@ -1,259 +1,255 @@ /* $OpenBSD: v831.c,v 1.11 2006/03/17 19:17:13 moritz Exp $ */ /* $NetBSD: v831.c,v 1.5 1996/12/29 10:42:01 cgd Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - /* * Routines for dialing up on Vadic 831 */ #include "tip.h" #include static jmp_buf jmpbuf; static pid_t child = -1; static void alarmtr(int); static int dialit(char *, char *); static char * sanitize(char *); int v831_dialer(char *num, char *acu) { int status; int timelim; pid_t pid; if (boolean(value(VERBOSE))) printf("\nstarting call..."); #ifdef DEBUG printf ("(acu=%s)\n", acu); #endif if ((AC = open(acu, O_RDWR)) < 0) { if (errno == EBUSY) printf("line busy..."); else printf("acu open error..."); return (0); } if (setjmp(jmpbuf)) { kill(child, SIGKILL); close(AC); return (0); } signal(SIGALRM, alarmtr); timelim = 5 * strlen(num); alarm(timelim < 30 ? 30 : timelim); if ((child = fork()) == 0) { /* * ignore this stuff for aborts */ signal(SIGALRM, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); sleep(2); exit(dialit(num, acu) != 'A'); } /* * open line - will return on carrier */ if ((FD = open(DV, O_RDWR)) < 0) { #ifdef DEBUG printf("(after open, errno=%d)\n", errno); #endif if (errno == EIO) printf("lost carrier..."); else printf("dialup line open failed..."); alarm(0); kill(child, SIGKILL); close(AC); return (0); } alarm(0); signal(SIGALRM, SIG_DFL); while ((pid = wait(&status)) != child && pid != -1) ; if (status) { close(AC); return (0); } return (1); } /*ARGSUSED*/ static void alarmtr(int signo) { alarm(0); longjmp(jmpbuf, 1); } /* * Insurance, for some reason we don't seem to be * hanging up... */ void v831_disconnect(void) { struct termios cntrl; sleep(2); #ifdef DEBUG printf("[disconnect: FD=%d]\n", FD); #endif if (FD > 0) { ioctl(FD, TIOCCDTR, 0); tcgetattr(FD, &cntrl); cfsetospeed(&cntrl, 0); cfsetispeed(&cntrl, 0); tcsetattr(FD, TCSAFLUSH, &cntrl); ioctl(FD, TIOCNXCL, NULL); } close(FD); } void v831_abort(void) { #ifdef DEBUG printf("[abort: AC=%d]\n", AC); #endif sleep(2); if (child > 0) kill(child, SIGKILL); if (FD > 0) ioctl(FD, TIOCNXCL, NULL); close(AC); if (FD > 0) ioctl(FD, TIOCCDTR, 0); close(FD); } /* * Sigh, this probably must be changed at each site. */ struct vaconfig { char *vc_name; char vc_rack; char vc_modem; } vaconfig[] = { { "/dev/cua0",'4','0' }, { "/dev/cua1",'4','1' }, { NULL, '\0', '\0' } }; #define pc(x) (c = x, write(AC,&c,1)) #define ABORT 01 #define SI 017 #define STX 02 #define ETX 03 static int dialit(char *phonenum, char *acu) { struct vaconfig *vp; struct termios cntrl; char c; int i; phonenum = sanitize(phonenum); #ifdef DEBUG printf ("(dial phonenum=%s)\n", phonenum); #endif if (*phonenum == '<' && phonenum[1] == 0) return ('Z'); for (vp = vaconfig; vp->vc_name; vp++) if (strcmp(vp->vc_name, acu) == 0) break; if (vp->vc_name == 0) { printf("Unable to locate dialer (%s)\n", acu); return ('K'); } tcgetattr(AC, &cntrl); cfsetospeed(&cntrl, B2400); cfsetispeed(&cntrl, B2400); cntrl.c_cflag |= PARODD | PARENB; cntrl.c_lflag &= ~(ISIG | ICANON); tcsetattr(AC, TCSANOW, &cntrl); tcflush(AC, TCIOFLUSH); pc(STX); pc(vp->vc_rack); pc(vp->vc_modem); while (*phonenum && *phonenum != '<') pc(*phonenum++); pc(SI); pc(ETX); sleep(1); i = read(AC, &c, 1); #ifdef DEBUG printf("read %d chars, char=%c, errno %d\n", i, c, errno); #endif if (i != 1) c = 'M'; if (c == 'B' || c == 'G') { char cc, oc = c; pc(ABORT); read(AC, &cc, 1); #ifdef DEBUG printf("abort response=%c\n", cc); #endif c = oc; v831_disconnect(); } close(AC); #ifdef DEBUG printf("dialit: returns %c\n", c); #endif return (c); } static char * sanitize(char *s) { static char buf[128]; char *cp; for (cp = buf; *s; s++) { if (!isdigit(*s) && *s == '<' && *s != '_') continue; if (*s == '_') *s = '='; *cp++ = *s; } *cp++ = 0; return (buf); } diff --git a/usr.bin/tip/libacu/ventel.c b/usr.bin/tip/libacu/ventel.c index 161dfa25d7d4..603c18e4b5ef 100644 --- a/usr.bin/tip/libacu/ventel.c +++ b/usr.bin/tip/libacu/ventel.c @@ -1,255 +1,251 @@ /* $OpenBSD: ventel.c,v 1.12 2006/03/17 19:17:13 moritz Exp $ */ /* $NetBSD: ventel.c,v 1.6 1997/02/11 09:24:21 mrg Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - /* * Routines for calling up on a Ventel Modem * The Ventel is expected to be strapped for local echo (just like uucp) */ #include "tip.h" #include #include #define MAXRETRY 5 static int dialtimeout = 0; static jmp_buf timeoutbuf; static void echo(char *); static void sigALRM(int); static int gobble(char, char *); static int vensync(int); /* * some sleep calls have been replaced by this macro * because some ventel modems require two s in less than * a second in order to 'wake up'... yes, it is dirty... */ #define delay(num,denom) busyloop(CPUSPEED*num/denom) #define CPUSPEED 1000000 /* VAX 780 is 1MIPS */ #define DELAY(n) do { long N = (n); while (--N > 0); } while (0) #define busyloop(n) do { DELAY(n); } while (0) int ven_dialer(char *num, char *acu) { char *cp; int connected = 0; char *msg, line[80]; struct termios cntrl; /* * Get in synch with a couple of carriage returns */ if (!vensync(FD)) { printf("can't synchronize with ventel\n"); #ifdef ACULOG logent(value(HOST), num, "ventel", "can't synch up"); #endif return (0); } if (boolean(value(VERBOSE))) printf("\ndialing..."); fflush(stdout); tcgetattr(FD, &cntrl); cntrl.c_cflag |= HUPCL; tcsetattr(FD, TCSANOW, &cntrl); echo("#k$\r$\n$D$I$A$L$:$ "); for (cp = num; *cp; cp++) { delay(1, 10); write(FD, cp, 1); } delay(1, 10); write(FD, "\r", 1); gobble('\n', line); if (gobble('\n', line)) connected = gobble('!', line); tcflush(FD, TCIOFLUSH); #ifdef ACULOG if (dialtimeout) { (void)snprintf(line, sizeof line, "%ld second dial timeout", number(value(DIALTIMEOUT))); logent(value(HOST), num, "ventel", line); } #endif if (dialtimeout) ven_disconnect(); /* insurance */ if (connected || dialtimeout || !boolean(value(VERBOSE))) return (connected); /* call failed, parse response for user */ cp = strchr(line, '\r'); if (cp) *cp = '\0'; for (cp = line; (cp = strchr(cp, ' ')) != NULL; cp++) if (cp[1] == ' ') break; if (cp) { while (*cp == ' ') cp++; msg = cp; while (*cp) { if (isupper(*cp)) *cp = tolower(*cp); cp++; } printf("%s...", msg); } return (connected); } void ven_disconnect(void) { close(FD); } void ven_abort(void) { write(FD, "\03", 1); close(FD); } static void echo(char *s) { char c; while ((c = *s++) != '\0') switch (c) { case '$': read(FD, &c, 1); s++; break; case '#': c = *s++; write(FD, &c, 1); break; default: write(FD, &c, 1); read(FD, &c, 1); } } /*ARGSUSED*/ static void sigALRM(int signo) { printf("\07timeout waiting for reply\n"); dialtimeout = 1; longjmp(timeoutbuf, 1); } static int gobble(char match, char response[]) { char *cp = response; sig_t f; char c; f = signal(SIGALRM, sigALRM); dialtimeout = 0; do { if (setjmp(timeoutbuf)) { signal(SIGALRM, f); *cp = '\0'; return (0); } alarm(number(value(DIALTIMEOUT))); read(FD, cp, 1); alarm(0); c = (*cp++ &= 0177); #ifdef notdef if (boolean(value(VERBOSE))) putchar(c); #endif } while (c != '\n' && c != match); signal(SIGALRM, SIG_DFL); *cp = '\0'; return (c == match); } #define min(a,b) ((a)>(b)?(b):(a)) /* * This convoluted piece of code attempts to get * the ventel in sync. If you don't have FIONREAD * there are gory ways to simulate this. */ static int vensync(int fd) { int already = 0, nread; char buf[60]; /* * Toggle DTR to force anyone off that might have left * the modem connected, and insure a consistent state * to start from. * * If you don't have the ioctl calls to diddle directly * with DTR, you can always try setting the baud rate to 0. */ ioctl(FD, TIOCCDTR, 0); sleep(1); ioctl(FD, TIOCSDTR, 0); while (already < MAXRETRY) { /* * After reseting the modem, send it two \r's to * autobaud on. Make sure to delay between them * so the modem can frame the incoming characters. */ write(fd, "\r", 1); delay(1,10); write(fd, "\r", 1); sleep(2); if (ioctl(fd, FIONREAD, (caddr_t)&nread) < 0) { perror("tip: ioctl"); continue; } while (nread > 0) { read(fd, buf, min(nread, 60)); if ((buf[nread - 1] & 0177) == '$') return (1); nread -= min(nread, 60); } sleep(1); already++; } return (0); } diff --git a/usr.bin/tip/tip/acu.c b/usr.bin/tip/tip/acu.c index dc88d5e6e416..a2c672eb888c 100644 --- a/usr.bin/tip/tip/acu.c +++ b/usr.bin/tip/tip/acu.c @@ -1,191 +1,187 @@ /* $OpenBSD: acu.c,v 1.12 2006/03/17 14:43:06 moritz Exp $ */ /* $NetBSD: acu.c,v 1.4 1996/12/29 10:34:03 cgd Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - #include "tip.h" static acu_t *acu = NOACU; static int conflag; static void acuabort(int); static acu_t *acutype(char *); static jmp_buf jmpbuf; /* * Establish connection for tip * * If DU is true, we should dial an ACU whose type is AT. * The phone numbers are in PN, and the call unit is in CU. * * If the PN is an '@', then we consult the PHONES file for * the phone numbers. This file is /etc/phones, unless overridden * by an exported shell variable. * * The data base files must be in the format: * host-name[ \t]*phone-number * with the possibility of multiple phone numbers * for a single host acting as a rotary (in the order * found in the file). */ char * con(void) { char *cp = PN; char *phnum, string[256]; FILE *fd; volatile int tried = 0; if (!DU) { /* regular connect message */ if (CM != NOSTR) parwrite(FD, CM, size(CM)); logent(value(HOST), "", DV, "call completed"); return (NOSTR); } /* * @ =>'s use data base in PHONES environment variable * otherwise, use /etc/phones */ signal(SIGINT, acuabort); signal(SIGQUIT, acuabort); if (setjmp(jmpbuf)) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); printf("\ncall aborted\n"); logent(value(HOST), "", "", "call aborted"); if (acu != NOACU) { setboolean(value(VERBOSE), FALSE); if (conflag) disconnect(NOSTR); else (*acu->acu_abort)(); } return ("interrupt"); } if ((acu = acutype(AT)) == NOACU) return ("unknown ACU type"); if (*cp != '@') { while (*cp) { phnum = cp; cp += strcspn(cp, ","); if (*cp != '\0') *cp++ = '\0'; if (strlen(phnum) == 0) continue; conflag = (*acu->acu_dialer)(phnum, CU); if (conflag) break; logent(value(HOST), phnum, acu->acu_name, "call failed"); tried++; } } else { if ((fd = fopen(PH, "r")) == NOFILE) { printf("%s: ", PH); return ("can't open phone number file"); } while (fgets(string, sizeof(string), fd) != NOSTR) { cp = &string[strcspn(string, " \t\n")]; if (*cp != '\0') *cp++ = '\0'; if (strcmp(string, value(HOST)) != 0) continue; cp += strspn(cp, " \t\n"); phnum = cp; *(cp + strcspn(cp, ",\n")) = '\0'; if (strlen(phnum) == 0) continue; conflag = (*acu->acu_dialer)(phnum, CU); if (conflag) break; logent(value(HOST), phnum, acu->acu_name, "call failed"); tried++; } fclose(fd); } if (conflag) { if (CM != NOSTR) parwrite(FD, CM, size(CM)); logent(value(HOST), phnum, acu->acu_name, "call completed"); return (NOSTR); } else if (!tried) { logent(value(HOST), "", acu->acu_name, "missing phone number"); return ("missing phone number"); } else { (*acu->acu_abort)(); return ("call failed"); } } void disconnect(char *reason) { if (!conflag) { logent(value(HOST), "", DV, "call terminated"); return; } if (reason == NOSTR) { logent(value(HOST), "", acu->acu_name, "call terminated"); if (boolean(value(VERBOSE))) printf("\r\ndisconnecting..."); } else logent(value(HOST), "", acu->acu_name, reason); (*acu->acu_disconnect)(); } static void acuabort(int s) { signal(s, SIG_IGN); longjmp(jmpbuf, 1); } static acu_t * acutype(char *s) { acu_t *p; extern acu_t acutable[]; for (p = acutable; p->acu_name != NULL; p++) if (!strcmp(s, p->acu_name)) return (p); return (NOACU); } diff --git a/usr.bin/tip/tip/acutab.c b/usr.bin/tip/tip/acutab.c index c5daf9525282..67cfad514731 100644 --- a/usr.bin/tip/tip/acutab.c +++ b/usr.bin/tip/tip/acutab.c @@ -1,85 +1,81 @@ /* $OpenBSD: acutab.c,v 1.5 2006/03/17 19:17:13 moritz Exp $ */ /* $NetBSD: acutab.c,v 1.3 1994/12/08 09:30:41 jtc Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - #include "tip.h" acu_t acutable[] = { #if BIZ1031 { "biz31f", biz31f_dialer, biz31_disconnect, biz31_abort }, { "biz31w", biz31w_dialer, biz31_disconnect, biz31_abort }, #endif #if BIZ1022 { "biz22f", biz22f_dialer, biz22_disconnect, biz22_abort }, { "biz22w", biz22w_dialer, biz22_disconnect, biz22_abort }, #endif #if DF02 { "df02", df02_dialer, df_disconnect, df_abort }, #endif #if DF03 { "df03", df03_dialer, df_disconnect, df_abort }, #endif #if DN11 { "dn11", dn_dialer, dn_disconnect, dn_abort }, #endif #ifdef VENTEL { "ventel", ven_dialer, ven_disconnect, ven_abort }, #endif #ifdef HAYES { "hayes", hay_dialer, hay_disconnect, hay_abort }, #endif #ifdef COURIER { "courier", cour_dialer, cour_disconnect, cour_abort }, #endif #ifdef T3000 { "t3000", t3000_dialer, t3000_disconnect, t3000_abort }, #endif #ifdef V3451 #ifndef V831 { "vadic", v3451_dialer, v3451_disconnect, v3451_abort }, #endif { "v3451", v3451_dialer, v3451_disconnect, v3451_abort }, #endif #ifdef V831 #ifndef V3451 { "vadic", v831_dialer, v831_disconnect, v831_abort }, #endif { "v831", v831_dialer, v831_disconnect, v831_abort }, #endif { 0, 0, 0, 0 } }; diff --git a/usr.bin/tip/tip/cmds.c b/usr.bin/tip/tip/cmds.c index ff53e5f0d2d1..28135f5accbd 100644 --- a/usr.bin/tip/tip/cmds.c +++ b/usr.bin/tip/tip/cmds.c @@ -1,995 +1,991 @@ /* $OpenBSD: cmds.c,v 1.26 2006/06/06 23:24:52 deraadt Exp $ */ /* $NetBSD: cmds.c,v 1.7 1997/02/11 09:24:03 mrg Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - #include "tip.h" #include "pathnames.h" #include /* * tip * * miscellaneous commands */ int quant[] = { 60, 60, 24 }; char null = '\0'; char *sep[] = { "second", "minute", "hour" }; static char *argv[10]; /* argument vector for take and put */ static void transfer(char *, int, char *); static void stopsnd(int); /* SIGINT handler during file transfers */ static void intcopy(int); /* interrupt routine for file transfers */ static void transmit(FILE *, char *, char *); static void send(int); static void execute(char *); static int args(char *, char **, int); static void prtime(char *, time_t); static void tandem(char *); static void hardwareflow(char *); void linedisc(char *); static int anyof(char *, char *); /* * FTP - remote ==> local * get a file from the remote host */ void getfl(int c) { char buf[256], *cp; putchar(c); /* * get the UNIX receiving file's name */ if (prompt("Local file name? ", copyname, sizeof(copyname))) return; cp = expand(copyname); if ((sfd = creat(cp, 0666)) < 0) { printf("\r\n%s: cannot creat\r\n", copyname); return; } /* * collect parameters */ if (prompt("List command for remote system? ", buf, sizeof(buf))) { unlink(copyname); return; } transfer(buf, sfd, value(EOFREAD)); } /* * Cu-like take command */ void cu_take(int c) { int fd, argc; char line[BUFSIZ], *cp; if (prompt("[take] ", copyname, sizeof(copyname))) return; if ((argc = args(copyname, argv, sizeof(argv)/sizeof(argv[0]))) < 1 || argc > 2) { printf("usage: from [to]\r\n"); return; } if (argc == 1) argv[1] = argv[0]; cp = expand(argv[1]); if ((fd = creat(cp, 0666)) < 0) { printf("\r\n%s: cannot create\r\n", argv[1]); return; } (void)snprintf(line, sizeof(line), "cat %s;echo ''|tr '\\012' '\\01'", argv[0]); transfer(line, fd, "\01"); } static jmp_buf intbuf; /* * Bulk transfer routine -- * used by getfl(), cu_take(), and pipefile() */ static void transfer(char *buf, int fd, char *eofchars) { int ct, eof; char c, buffer[BUFSIZ]; char *p = buffer; size_t cnt; time_t start; sig_t f; char r; if (number(value(FRAMESIZE)) > BUFSIZ || number(value(FRAMESIZE)) < 1) { printf("framesize must be >= 1 and <= %d\r\n", BUFSIZ); close(fd); return; } parwrite(FD, buf, size(buf)); quit = 0; kill(tipout_pid, SIGIOT); read(repdes[0], (char *)&ccc, 1); /* Wait until read process stops */ /* * finish command */ r = '\r'; parwrite(FD, &r, 1); do read(FD, &c, 1); while ((c&STRIP_PAR) != '\n'); tcsetattr(0, TCSAFLUSH, &defchars); (void) setjmp(intbuf); f = signal(SIGINT, intcopy); start = time(0); for (ct = 0; !quit;) { eof = read(FD, &c, 1) <= 0; c &= STRIP_PAR; if (quit) continue; if (eof || any(c, eofchars)) break; if (c == 0) continue; /* ignore nulls */ if (c == '\r') continue; *p++ = c; if (c == '\n' && boolean(value(VERBOSE))) printf("\r%d", ++ct); if ((cnt = (p-buffer)) == (size_t)number(value(FRAMESIZE))) { if ((size_t)write(fd, buffer, cnt) != cnt) { printf("\r\nwrite error\r\n"); quit = 1; } p = buffer; } } if ((cnt = (p-buffer))) if ((size_t)write(fd, buffer, cnt) != cnt) printf("\r\nwrite error\r\n"); if (boolean(value(VERBOSE))) prtime(" lines transferred in ", time(0)-start); tcsetattr(0, TCSAFLUSH, &term); write(fildes[1], (char *)&ccc, 1); signal(SIGINT, f); close(fd); } /* * FTP - remote ==> local process * send remote input to local process via pipe */ /*ARGSUSED*/ void pipefile(int c) { int pdes[2]; char buf[256]; int status, p; pid_t cpid; if (prompt("Local command? ", buf, sizeof(buf))) return; if (pipe(pdes)) { printf("can't establish pipe\r\n"); return; } if ((cpid = fork()) < 0) { printf("can't fork!\r\n"); return; } else if (cpid) { if (prompt("List command for remote system? ", buf, sizeof(buf))) { close(pdes[0]), close(pdes[1]); kill (cpid, SIGKILL); } else { close(pdes[0]); signal(SIGPIPE, intcopy); transfer(buf, pdes[1], value(EOFREAD)); signal(SIGPIPE, SIG_DFL); while ((p = wait(&status)) > 0 && p != cpid) ; } } else { int f; dup2(pdes[0], 0); close(pdes[0]); for (f = 3; f < 20; f++) close(f); execute(buf); printf("can't execl!\r\n"); exit(0); } } /* * Interrupt service routine for FTP */ /*ARGSUSED*/ static void stopsnd(int signo) { stop = 1; signal(SIGINT, SIG_IGN); } /* * FTP - local ==> remote * send local file to remote host * terminate transmission with pseudo EOF sequence */ void sendfile(int c) { FILE *fp; char *fnamex; putchar(c); /* * get file name */ if (prompt("Local file name? ", fname, sizeof(fname))) return; /* * look up file */ fnamex = expand(fname); if ((fp = fopen(fnamex, "r")) == NULL) { printf("%s: cannot open\r\n", fname); return; } transmit(fp, value(EOFWRITE), NULL); if (!boolean(value(ECHOCHECK))) tcdrain(FD); } /* * Bulk transfer routine to remote host -- * used by sendfile() and cu_put() */ static void transmit(FILE *fp, char *eofchars, char *command) { char *pc, lastc; int c, ccount, lcount; time_t start_t, stop_t; sig_t f; kill(tipout_pid, SIGIOT); /* put TIPOUT into a wait state */ stop = 0; f = signal(SIGINT, stopsnd); tcsetattr(0, TCSAFLUSH, &defchars); read(repdes[0], (char *)&ccc, 1); if (command != NULL) { for (pc = command; *pc; pc++) send(*pc); if (boolean(value(ECHOCHECK))) read(FD, (char *)&c, 1); /* trailing \n */ else { tcdrain(FD); sleep(5); /* wait for remote stty to take effect */ } } lcount = 0; lastc = '\0'; start_t = time(0); while (1) { ccount = 0; do { c = getc(fp); if (stop) goto out; if (c == EOF) goto out; if (c == 0177 && !boolean(value(RAWFTP))) continue; lastc = c; if (c < 040) { if (c == '\n') { if (!boolean(value(RAWFTP))) c = '\r'; } else if (c == '\t') { if (!boolean(value(RAWFTP))) { if (boolean(value(TABEXPAND))) { send(' '); while ((++ccount % 8) != 0) send(' '); continue; } } } else if (!boolean(value(RAWFTP))) continue; } send(c); } while (c != '\r' && !boolean(value(RAWFTP))); if (boolean(value(VERBOSE))) printf("\r%d", ++lcount); if (boolean(value(ECHOCHECK))) { timedout = 0; alarm((unsigned int)lvalue(ETIMEOUT)); do { /* wait for prompt */ read(FD, (char *)&c, 1); if (timedout || stop) { if (timedout) printf("\r\ntimed out at eol\r\n"); alarm(0); goto out; } } while ((c&STRIP_PAR) != character(value(PROMPT))); alarm(0); } } out: if (lastc != '\n' && !boolean(value(RAWFTP))) send('\r'); if (eofchars) { for (pc = eofchars; *pc; pc++) send(*pc); } stop_t = time(0); fclose(fp); signal(SIGINT, f); if (boolean(value(VERBOSE))) { if (boolean(value(RAWFTP))) prtime(" chars transferred in ", stop_t-start_t); else prtime(" lines transferred in ", stop_t-start_t); } write(fildes[1], (char *)&ccc, 1); tcsetattr(0, TCSAFLUSH, &term); } /* * Cu-like put command */ /*ARGSUSED*/ void cu_put(int c) { FILE *fp; char line[BUFSIZ]; int argc; char *copynamex; if (prompt("[put] ", copyname, sizeof(copyname))) return; if ((argc = args(copyname, argv, sizeof(argv)/sizeof(argv[0]))) < 1 || argc > 2) { printf("usage: from [to]\r\n"); return; } if (argc == 1) argv[1] = argv[0]; copynamex = expand(argv[0]); if ((fp = fopen(copynamex, "r")) == NULL) { printf("%s: cannot open\r\n", copynamex); return; } if (boolean(value(ECHOCHECK))) (void)snprintf(line, sizeof(line), "cat>%s\r", argv[1]); else (void)snprintf(line, sizeof(line), "stty -echo;cat>%s;stty echo\r", argv[1]); transmit(fp, "\04", line); } /* * FTP - send single character * wait for echo & handle timeout */ static void send(int c) { char cc; int retry = 0; cc = c; parwrite(FD, &cc, 1); if (number(value(CDELAY)) > 0 && c != '\r') usleep(number(value(CDELAY))); if (!boolean(value(ECHOCHECK))) { if (number(value(LDELAY)) > 0 && c == '\r') usleep(number(value(LDELAY))); return; } tryagain: timedout = 0; alarm((unsigned int)lvalue(ETIMEOUT)); read(FD, &cc, 1); alarm(0); if (timedout) { printf("\r\ntimeout error (%s)\r\n", ctrl(c)); if (retry++ > 3) return; parwrite(FD, &null, 1); /* poke it */ goto tryagain; } } /*ARGSUSED*/ void timeout(int signo) { signal(SIGALRM, timeout); timedout = 1; } /* * Stolen from consh() -- puts a remote file on the output of a local command. * Identical to consh() except for where stdout goes. */ void pipeout(int c) { char buf[256]; int status, p; pid_t cpid; time_t start = time(NULL); putchar(c); if (prompt("Local command? ", buf, sizeof(buf))) return; kill(tipout_pid, SIGIOT); /* put TIPOUT into a wait state */ signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); tcsetattr(0, TCSAFLUSH, &defchars); read(repdes[0], (char *)&ccc, 1); /* * Set up file descriptors in the child and * let it go... */ if ((cpid = fork()) < 0) printf("can't fork!\r\n"); else if (cpid) { start = time(NULL); while ((p = wait(&status)) > 0 && p != cpid) ; } else { int i; dup2(FD, 1); for (i = 3; i < 20; i++) close(i); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); execute(buf); printf("can't find `%s'\r\n", buf); exit(0); } if (boolean(value(VERBOSE))) prtime("away for ", time(0)-start); write(fildes[1], (char *)&ccc, 1); tcsetattr(0, TCSAFLUSH, &term); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); } #ifdef CONNECT /* * Fork a program with: * 0 <-> remote tty in * 1 <-> remote tty out * 2 <-> local tty stderr */ void consh(int c) { char buf[256]; int status, p; pid_t cpid; time_t start = time(NULL); putchar(c); if (prompt("Local command? ", buf, sizeof(buf))) return; kill(tipout_pid, SIGIOT); /* put TIPOUT into a wait state */ signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); tcsetattr(0, TCSAFLUSH, &defchars); read(repdes[0], (char *)&ccc, 1); /* * Set up file descriptors in the child and * let it go... */ if ((cpid = fork()) < 0) printf("can't fork!\r\n"); else if (cpid) { start = time(0); while ((p = wait(&status)) > 0 && p != cpid) ; } else { int i; dup2(FD, 0); dup2(3, 1); for (i = 3; i < 20; i++) close(i); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); execute(buf); printf("can't find `%s'\r\n", buf); exit(0); } if (boolean(value(VERBOSE))) prtime("away for ", time(0)-start); write(fildes[1], (char *)&ccc, 1); tcsetattr(0, TCSAFLUSH, &term); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); } #endif /* * Escape to local shell */ /*ARGSUSED*/ void shell(int c) { int status; char *cp; pid_t shpid; printf("[sh]\r\n"); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); unraw(); if ((shpid = fork())) { while (shpid != wait(&status)); raw(); printf("\r\n!\r\n"); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); return; } else { signal(SIGQUIT, SIG_DFL); signal(SIGINT, SIG_DFL); if ((cp = strrchr(value(SHELL), '/')) == NULL) cp = value(SHELL); else cp++; shell_uid(); execl(value(SHELL), cp, (char *)NULL); printf("\r\ncan't execl!\r\n"); exit(1); } } /* * TIPIN portion of scripting * initiate the conversation with TIPOUT */ void setscript(void) { char c; /* * enable TIPOUT side for dialogue */ kill(tipout_pid, SIGEMT); if (boolean(value(SCRIPT))) write(fildes[1], value(RECORD), size(value(RECORD))); write(fildes[1], "\n", 1); /* * wait for TIPOUT to finish */ read(repdes[0], &c, 1); if (c == 'n') printf("can't create %s\r\n", value(RECORD)); } /* * Change current working directory of * local portion of tip */ /*ARGSUSED*/ void chdirectory(int c) { char dirname[PATH_MAX]; char *cp = dirname; if (prompt("[cd] ", dirname, sizeof(dirname))) { if (stoprompt) return; cp = value(HOME); } if (chdir(cp) < 0) printf("%s: bad directory\r\n", cp); printf("!\r\n"); } void tipabort(char *msg) { signal(SIGTERM, SIG_IGN); kill(tipout_pid, SIGTERM); disconnect(msg); if (msg != NOSTR) printf("\r\n%s", msg); printf("\r\n[EOT]\r\n"); daemon_uid(); (void)uu_unlock(uucplock); unraw(); unexcl(); exit(0); } /*ARGSUSED*/ void finish(int c) { char *dismsg; if ((dismsg = value(DISCONNECT)) != NOSTR) { write(FD, dismsg, strlen(dismsg)); sleep(5); } tipabort(NOSTR); } /*ARGSUSED*/ static void intcopy(int signo) { raw(); quit = 1; longjmp(intbuf, 1); } static void execute(char *s) { char *cp; if ((cp = strrchr(value(SHELL), '/')) == NULL) cp = value(SHELL); else cp++; shell_uid(); execl(value(SHELL), cp, "-c", s, (char *)NULL); } static int args(char *buf, char *a[], int num) { char *p = buf, *start; char **parg = a; int n = 0; do { while (*p && (*p == ' ' || *p == '\t')) p++; start = p; if (*p) *parg = p; while (*p && (*p != ' ' && *p != '\t')) p++; if (p != start) parg++, n++; if (*p) *p++ = '\0'; } while (*p && n < num); return(n); } static void prtime(char *s, time_t a) { int i; int nums[3]; for (i = 0; i < 3; i++) { nums[i] = (int)(a % quant[i]); a /= quant[i]; } printf("%s", s); while (--i >= 0) if (nums[i] || (i == 0 && nums[1] == 0 && nums[2] == 0)) printf("%d %s%c ", nums[i], sep[i], nums[i] == 1 ? '\0' : 's'); printf("\r\n!\r\n"); } /*ARGSUSED*/ void variable(int c) { char buf[256]; if (prompt("[set] ", buf, sizeof(buf))) return; vlex(buf); if (vtable[BEAUTIFY].v_access&CHANGED) { vtable[BEAUTIFY].v_access &= ~CHANGED; kill(tipout_pid, SIGSYS); } if (vtable[SCRIPT].v_access&CHANGED) { vtable[SCRIPT].v_access &= ~CHANGED; setscript(); /* * So that "set record=blah script" doesn't * cause two transactions to occur. */ if (vtable[RECORD].v_access&CHANGED) vtable[RECORD].v_access &= ~CHANGED; } if (vtable[RECORD].v_access&CHANGED) { vtable[RECORD].v_access &= ~CHANGED; if (boolean(value(SCRIPT))) setscript(); } if (vtable[TAND].v_access&CHANGED) { vtable[TAND].v_access &= ~CHANGED; if (boolean(value(TAND))) tandem("on"); else tandem("off"); } if (vtable[LECHO].v_access&CHANGED) { vtable[LECHO].v_access &= ~CHANGED; HD = boolean(value(LECHO)); } if (vtable[PARITY].v_access&CHANGED) { vtable[PARITY].v_access &= ~CHANGED; setparity(NOSTR); } if (vtable[HARDWAREFLOW].v_access&CHANGED) { vtable[HARDWAREFLOW].v_access &= ~CHANGED; if (boolean(value(HARDWAREFLOW))) hardwareflow("on"); else hardwareflow("off"); } if (vtable[LINEDISC].v_access&CHANGED) { vtable[LINEDISC].v_access &= ~CHANGED; linedisc(NOSTR); } } /*ARGSUSED*/ void listvariables(int c) { value_t *p; char *buf; char charbuf[5]; /* for vis(3), 4 chars for encoding, plus nul */ puts("v\r"); for (p = vtable; p->v_name; p++) { fputs(p->v_name, stdout); switch (p->v_type&TMASK) { case STRING: if (p->v_value) { buf = malloc(4*strlen(p->v_value) + 1); if (buf == NULL) { fprintf(stderr, "Unable to malloc()\n"); abort(); } strvis(buf, p->v_value, VIS_WHITE); printf(" %s", buf); free(buf); } putchar('\r'); putchar('\n'); break; case NUMBER: printf(" %ld\r\n", number(p->v_value)); break; case BOOL: printf(" %s\r\n", !boolean(p->v_value) ? "false" : "true"); break; case CHAR: vis(charbuf, character(p->v_value), VIS_WHITE, 0); printf(" %s\r\n", charbuf); break; } } } /* * Turn tandem mode on or off for remote tty. */ static void tandem(char *option) { struct termios rmtty; tcgetattr(FD, &rmtty); if (strcmp(option, "on") == 0) { rmtty.c_iflag |= IXOFF; term.c_iflag |= IXOFF; } else { rmtty.c_iflag &= ~IXOFF; term.c_iflag &= ~IXOFF; } tcsetattr(FD, TCSADRAIN, &rmtty); tcsetattr(0, TCSADRAIN, &term); } /* * Turn hardware flow control on or off for remote tty. */ static void hardwareflow(char *option) { struct termios rmtty; tcgetattr(FD, &rmtty); if (strcmp(option, "on") == 0) rmtty.c_iflag |= CRTSCTS; else rmtty.c_iflag &= ~CRTSCTS; tcsetattr(FD, TCSADRAIN, &rmtty); } /* * Change line discipline to the specified one. */ void linedisc(char *option) { int ld = (int)(intptr_t)value(LINEDISC); ioctl(FD, TIOCSETD, &ld); } /* * Send a break. */ /*ARGSUSED*/ void genbrk(int c) { ioctl(FD, TIOCSBRK, NULL); sleep(1); ioctl(FD, TIOCCBRK, NULL); } /* * Suspend tip */ void suspend(int c) { unraw(); kill(c == CTRL('y') ? getpid() : 0, SIGTSTP); raw(); } /* * expand a file name if it includes shell meta characters */ char * expand(char name[]) { static char xname[BUFSIZ]; char cmdbuf[BUFSIZ]; int l; char *cp, *Shell; int s, pivec[2]; pid_t pid; if (!anyof(name, "~{[*?$`'\"\\")) return(name); /* sigint = signal(SIGINT, SIG_IGN); */ if (pipe(pivec) < 0) { perror("pipe"); /* signal(SIGINT, sigint) */ return(name); } (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name); if ((pid = vfork()) == 0) { Shell = value(SHELL); if (Shell == NOSTR) Shell = _PATH_BSHELL; close(pivec[0]); close(1); dup(pivec[1]); close(pivec[1]); close(2); shell_uid(); execl(Shell, Shell, "-c", cmdbuf, (char *)NULL); _exit(1); } if (pid == -1) { perror("fork"); close(pivec[0]); close(pivec[1]); return(NOSTR); } close(pivec[1]); l = read(pivec[0], xname, BUFSIZ); close(pivec[0]); while (wait(&s) != pid); ; s &= 0377; if (s != 0 && s != SIGPIPE) { fprintf(stderr, "\"Echo\" failed\n"); return(NOSTR); } if (l < 0) { perror("read"); return(NOSTR); } if (l == 0) { fprintf(stderr, "\"%s\": No match\n", name); return(NOSTR); } if (l == BUFSIZ) { fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name); return(NOSTR); } xname[l] = 0; for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) ; *++cp = '\0'; return(xname); } /* * Are any of the characters in the two strings the same? */ static int anyof(char *s1, char *s2) { int c; while ((c = *s1++)) if (any(c, s2)) return(1); return(0); } diff --git a/usr.bin/tip/tip/cmdtab.c b/usr.bin/tip/tip/cmdtab.c index b73e4c045b2b..7219a45355ff 100644 --- a/usr.bin/tip/tip/cmdtab.c +++ b/usr.bin/tip/tip/cmdtab.c @@ -1,62 +1,58 @@ /* $OpenBSD: cmdtab.c,v 1.7 2006/03/17 14:43:06 moritz Exp $ */ /* $NetBSD: cmdtab.c,v 1.3 1994/12/08 09:30:46 jtc Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - #include "tip.h" esctable_t etable[] = { { '!', NORM, "shell", shell }, { '<', NORM, "receive file from remote host", getfl }, { '>', NORM, "send file to remote host", sendfile }, { 't', NORM, "take file from remote UNIX", cu_take }, { 'p', NORM, "put file to remote UNIX", cu_put }, { '|', NORM, "pipe remote file", pipefile }, { '$', NORM, "pipe local command to remote host", pipeout }, #ifdef CONNECT { 'C', NORM, "connect program to remote host",consh }, #endif { 'c', NORM, "change directory", chdirectory }, { '.', NORM, "exit from tip", finish }, {CTRL('d'),NORM,"exit from tip", finish }, {CTRL('y'),NORM,"suspend tip (local+remote)", suspend }, {CTRL('z'),NORM,"suspend tip (local only)", suspend }, { 's', NORM, "set variable", variable }, { 'v', NORM, "list variables", listvariables }, { '?', NORM, "get this summary", help }, { '#', NORM, "send break", genbrk }, { '\0', 0, NULL, NULL } }; diff --git a/usr.bin/tip/tip/cu.c b/usr.bin/tip/tip/cu.c index 03a7de18c443..6811432d19c3 100644 --- a/usr.bin/tip/tip/cu.c +++ b/usr.bin/tip/tip/cu.c @@ -1,206 +1,202 @@ /* $OpenBSD: cu.c,v 1.19 2006/05/25 08:41:52 jmc Exp $ */ /* $NetBSD: cu.c,v 1.5 1997/02/11 09:24:05 mrg Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - #include "tip.h" static void cuusage(void); /* * Botch the interface to look like cu's */ void cumain(int argc, char *argv[]) { int ch, i, parity; long l; char *cp; static char sbuf[12]; if (argc < 2) cuusage(); CU = DV = NOSTR; BR = DEFBR; parity = 0; /* none */ /* * We want to accept -# as a speed. It's easiest to look through * the arguments, replace -# with -s#, and let getopt() handle it. */ for (i = 1; i < argc; i++) { if (argv[i][0] == '-' && argv[i][1] >= '0' && argv[i][1] <= '9') { asprintf(&cp, "-s%s", argv[i] + 1); if (cp == NULL) { fprintf(stderr, "%s: cannot convert -# to -s#\n", __progname); exit(3); } argv[i] = cp; } } while ((ch = getopt(argc, argv, "a:l:s:htoe")) != -1) { switch (ch) { case 'a': CU = optarg; break; case 'l': if (DV != NULL) { fprintf(stderr, "%s: cannot specificy multiple -l options\n", __progname); exit(3); } if (strchr(optarg, '/')) DV = optarg; else asprintf(&DV, "/dev/%s", optarg); break; case 's': l = strtol(optarg, &cp, 10); if (*cp != '\0' || l < 0 || l >= INT_MAX) { fprintf(stderr, "%s: unsupported speed %s\n", __progname, optarg); exit(3); } BR = (int)l; break; case 'h': setboolean(value(LECHO), TRUE); HD = TRUE; break; case 't': HW = 1, DU = -1; break; case 'o': if (parity != 0) parity = 0; /* -e -o */ else parity = 1; /* odd */ break; case 'e': if (parity != 0) parity = 0; /* -o -e */ else parity = -1; /* even */ break; default: cuusage(); break; } } argc -= optind; argv += optind; switch (argc) { case 1: PN = argv[0]; break; case 0: break; default: cuusage(); break; } signal(SIGINT, cleanup); signal(SIGQUIT, cleanup); signal(SIGHUP, cleanup); signal(SIGTERM, cleanup); signal(SIGCHLD, SIG_DFL); /* * The "cu" host name is used to define the * attributes of the generic dialer. */ (void)snprintf(sbuf, sizeof(sbuf), "cu%ld", BR); if ((i = hunt(sbuf)) == 0) { printf("all ports busy\n"); exit(3); } if (i == -1) { printf("link down\n"); (void)uu_unlock(uucplock); exit(3); } setbuf(stdout, NULL); loginit(); user_uid(); vinit(); switch (parity) { case -1: setparity("even"); break; case 1: setparity("odd"); break; default: setparity("none"); break; } setboolean(value(VERBOSE), FALSE); if (HW && ttysetup(BR)) { fprintf(stderr, "%s: unsupported speed %ld\n", __progname, BR); daemon_uid(); (void)uu_unlock(uucplock); exit(3); } if (con()) { printf("Connect failed\n"); daemon_uid(); (void)uu_unlock(uucplock); exit(1); } if (!HW && ttysetup(BR)) { fprintf(stderr, "%s: unsupported speed %ld\n", __progname, BR); daemon_uid(); (void)uu_unlock(uucplock); exit(3); } } static void cuusage(void) { fprintf(stderr, "usage: cu [-ehot] [-a acu] [-l line] " "[-s speed | -speed] [phone-number]\n"); exit(8); } diff --git a/usr.bin/tip/tip/hunt.c b/usr.bin/tip/tip/hunt.c index 75aa65e7c7ce..f4795091cb88 100644 --- a/usr.bin/tip/tip/hunt.c +++ b/usr.bin/tip/tip/hunt.c @@ -1,105 +1,101 @@ /* $OpenBSD: hunt.c,v 1.13 2006/03/17 19:39:46 deraadt Exp $ */ /* $NetBSD: hunt.c,v 1.6 1997/04/20 00:02:10 mellon Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - #include "tip.h" static jmp_buf deadline; static int deadfl; static void dead(int); /*ARGSUSED*/ static void dead(int signo) { deadfl = 1; longjmp(deadline, 1); } long hunt(char *name) { char *cp; sig_t f; f = signal(SIGALRM, dead); while ((cp = getremote(name))) { deadfl = 0; uucplock = strrchr(cp, '/'); if (uucplock == NULL) uucplock = cp; else uucplock++; if (uu_lock(uucplock) < 0) continue; /* * Straight through call units, such as the BIZCOMP, * VADIC and the DF, must indicate they're hardwired in * order to get an open file descriptor placed in FD. * Otherwise, as for a DN-11, the open will have to * be done in the "open" routine. */ if (!HW) break; if (setjmp(deadline) == 0) { alarm(10); FD = open(cp, (O_RDWR | (boolean(value(DC)) ? O_NONBLOCK : 0))); } alarm(0); if (FD < 0) { perror(cp); deadfl = 1; } if (!deadfl) { struct termios cntrl; tcgetattr(FD, &cntrl); if (!boolean(value(DC))) cntrl.c_cflag |= HUPCL; tcsetattr(FD, TCSAFLUSH, &cntrl); ioctl(FD, TIOCEXCL, 0); signal(SIGALRM, SIG_DFL); return ((long)cp); } (void)uu_unlock(uucplock); } signal(SIGALRM, f); return (deadfl ? -1 : (long)cp); } diff --git a/usr.bin/tip/tip/log.c b/usr.bin/tip/tip/log.c index 28cfa67af7a8..5a3dc6e25ddc 100644 --- a/usr.bin/tip/tip/log.c +++ b/usr.bin/tip/tip/log.c @@ -1,88 +1,84 @@ /* $OpenBSD: log.c,v 1.8 2006/03/16 19:32:46 deraadt Exp $ */ /* $NetBSD: log.c,v 1.4 1994/12/24 17:56:28 cgd Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - #include "tip.h" #ifdef ACULOG static FILE *flog = NULL; /* * Log file maintenance routines */ void logent(char *group, char *num, char *acu, char *message) { char *user, *timestamp; struct passwd *pwd; time_t t; if (flog == NULL) return; if (flock(fileno(flog), LOCK_EX) < 0) { perror("flock"); return; } if ((user = getlogin()) == NOSTR) { if ((pwd = getpwuid(getuid())) == NOPWD) user = "???"; else user = pwd->pw_name; } t = time(0); timestamp = ctime(&t); timestamp[24] = '\0'; fprintf(flog, "%s (%s) <%s, %s, %s> %s\n", user, timestamp, group, #ifdef PRISTINE "", #else num, #endif acu, message); (void) fflush(flog); (void) flock(fileno(flog), LOCK_UN); } void loginit(void) { flog = fopen(value(LOG), "a"); if (flog == NULL) fprintf(stderr, "can't open log file %s.\r\n", value(LOG)); } #endif diff --git a/usr.bin/tip/tip/partab.c b/usr.bin/tip/tip/partab.c index f4e8bfb00745..c15482811c67 100644 --- a/usr.bin/tip/tip/partab.c +++ b/usr.bin/tip/tip/partab.c @@ -1,59 +1,55 @@ /* $OpenBSD: partab.c,v 1.5 2003/06/03 02:56:18 millert Exp $ */ /* $NetBSD: partab.c,v 1.4 1996/12/29 10:38:21 cgd Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - /* * Even parity table for 0-0177 */ const unsigned char evenpartab[] = { 0000,0201,0202,0003,0204,0005,0006,0207, 0210,0011,0012,0213,0014,0215,0216,0017, 0220,0021,0022,0223,0024,0225,0226,0027, 0030,0231,0232,0033,0234,0035,0036,0237, 0240,0041,0042,0243,0044,0245,0246,0047, 0050,0251,0252,0053,0254,0055,0056,0257, 0060,0261,0262,0063,0264,0065,0066,0267, 0270,0071,0072,0273,0074,0275,0276,0077, 0300,0101,0102,0303,0104,0305,0306,0107, 0110,0311,0312,0113,0314,0115,0116,0317, 0120,0321,0322,0123,0324,0125,0126,0327, 0330,0131,0132,0333,0134,0335,0336,0137, 0140,0341,0342,0143,0344,0145,0146,0347, 0350,0151,0152,0353,0154,0355,0356,0157, 0360,0161,0162,0363,0164,0365,0366,0167, 0170,0371,0372,0173,0374,0175,0176,0377, }; diff --git a/usr.bin/tip/tip/tipout.c b/usr.bin/tip/tip/tipout.c index 8da6ad0633e1..be0d5c7c48e2 100644 --- a/usr.bin/tip/tip/tipout.c +++ b/usr.bin/tip/tip/tipout.c @@ -1,184 +1,180 @@ /* $OpenBSD: tipout.c,v 1.18 2006/05/31 07:03:08 jason Exp $ */ /* $NetBSD: tipout.c,v 1.5 1996/12/29 10:34:12 cgd Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - #include "tip.h" /* * tip * * lower fork of tip -- handles passive side * reading from the remote host */ static jmp_buf sigbuf; static void intIOT(int); static void intEMT(int); static void intTERM(int); static void intSYS(int); /* * TIPOUT wait state routine -- * sent by TIPIN when it wants to posses the remote host */ /*ARGSUSED*/ static void intIOT(int signo) { write(repdes[1],&ccc,1); read(fildes[0], &ccc,1); longjmp(sigbuf, 1); } /* * Scripting command interpreter -- * accepts script file name over the pipe and acts accordingly */ /*ARGSUSED*/ static void intEMT(int signo) { char c, line[256]; char *pline = line; char reply; read(fildes[0], &c, 1); while (c != '\n' && (size_t)(pline - line) < sizeof(line)) { *pline++ = c; read(fildes[0], &c, 1); } *pline = '\0'; if (boolean(value(SCRIPT)) && fscript != NULL) fclose(fscript); if (pline == line) { setboolean(value(SCRIPT), FALSE); reply = 'y'; } else { if ((fscript = fopen(line, "a")) == NULL) reply = 'n'; else { reply = 'y'; setboolean(value(SCRIPT), TRUE); } } write(repdes[1], &reply, 1); longjmp(sigbuf, 1); } static void intTERM(int signo) { if (boolean(value(SCRIPT)) && fscript != NULL) fclose(fscript); if (signo && tipin_pid) kill(tipin_pid, signo); exit(0); } /*ARGSUSED*/ static void intSYS(int signo) { setboolean(value(BEAUTIFY), !boolean(value(BEAUTIFY))); longjmp(sigbuf, 1); } /* * ****TIPOUT TIPOUT**** */ void tipout(void) { char buf[BUFSIZ]; char *cp; ssize_t scnt; size_t cnt; sigset_t mask, omask; signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGEMT, intEMT); /* attention from TIPIN */ signal(SIGTERM, intTERM); /* time to go signal */ signal(SIGIOT, intIOT); /* scripting going on signal */ signal(SIGHUP, intTERM); /* for dial-ups */ signal(SIGSYS, intSYS); /* beautify toggle */ (void) setjmp(sigbuf); sigprocmask(SIG_BLOCK, NULL, &omask); for (;;) { sigprocmask(SIG_SETMASK, &omask, NULL); scnt = read(FD, buf, BUFSIZ); if (scnt <= 0) { /* lost carrier */ if (scnt == 0 || (scnt < 0 && (errno == EIO || errno == ENXIO))) { sigemptyset(&mask); sigaddset(&mask, SIGTERM); sigprocmask(SIG_BLOCK, &mask, NULL); intTERM(SIGHUP); /*NOTREACHED*/ } continue; } cnt = scnt; sigemptyset(&mask); sigaddset(&mask, SIGEMT); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGIOT); sigaddset(&mask, SIGSYS); sigprocmask(SIG_BLOCK, &mask, NULL); for (cp = buf; cp < buf + cnt; cp++) *cp &= STRIP_PAR; write(STDOUT_FILENO, buf, cnt); if (boolean(value(SCRIPT)) && fscript != NULL) { if (!boolean(value(BEAUTIFY))) { fwrite(buf, 1, cnt, fscript); } else { for (cp = buf; cp < buf + cnt; cp++) if ((*cp >= ' ' && *cp <= '~') || any(*cp, value(EXCEPTIONS))) putc(*cp, fscript); } for (cp = buf; cp < buf + cnt; cp++) { if (!isgraph(*cp)) { fflush(fscript); break; } } } } } diff --git a/usr.bin/tip/tip/uucplock.c b/usr.bin/tip/tip/uucplock.c index 6d4f262ed146..e29cbc149274 100644 --- a/usr.bin/tip/tip/uucplock.c +++ b/usr.bin/tip/tip/uucplock.c @@ -1,127 +1,123 @@ /* $OpenBSD: uucplock.c,v 1.11 2006/03/16 19:32:46 deraadt Exp $ */ /* $NetBSD: uucplock.c,v 1.7 1997/02/11 09:24:08 mrg Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - #include #include #include #include #include #include #include #include #include #include "tip.h" #include "pathnames.h" /* * uucp style locking routines * return: 0 - success * -1 - failure */ int uu_lock(char *ttyname) { int fd, len; char tbuf[sizeof(_PATH_LOCKDIRNAME) + MAXNAMLEN]; char text_pid[81]; pid_t pid; (void)snprintf(tbuf, sizeof tbuf, _PATH_LOCKDIRNAME, ttyname); fd = open(tbuf, O_RDWR|O_CREAT|O_EXCL, 0660); if (fd < 0) { /* * file is already locked * check to see if the process holding the lock still exists */ fd = open(tbuf, O_RDWR, 0); if (fd < 0) { perror(tbuf); fprintf(stderr, "Can't open lock file.\n"); return(-1); } len = read(fd, text_pid, sizeof(text_pid)-1); if (len<=0) { perror(tbuf); (void)close(fd); fprintf(stderr, "Can't read lock file.\n"); return(-1); } text_pid[len] = 0; pid = atol(text_pid); if (kill(pid, 0) == 0 || errno != ESRCH) { (void)close(fd); /* process is still running */ return(-1); } /* * The process that locked the file isn't running, so * we'll lock it ourselves */ fprintf(stderr, "Stale lock on %s PID=%ld... overriding.\n", ttyname, (long)pid); if (lseek(fd, (off_t)0, SEEK_SET) < 0) { perror(tbuf); (void)close(fd); fprintf(stderr, "Can't seek lock file.\n"); return(-1); } /* fall out and finish the locking process */ } pid = getpid(); (void)snprintf(text_pid, sizeof text_pid, "%10ld\n", (long)pid); len = strlen(text_pid); if (write(fd, text_pid, len) != len) { (void)close(fd); (void)unlink(tbuf); perror("lock write"); return(-1); } (void)close(fd); return(0); } int uu_unlock(char *ttyname) { char tbuf[sizeof(_PATH_LOCKDIRNAME) + MAXNAMLEN]; (void)snprintf(tbuf, sizeof tbuf, _PATH_LOCKDIRNAME, ttyname); unexcl(); return(unlink(tbuf)); } diff --git a/usr.bin/tip/tip/value.c b/usr.bin/tip/tip/value.c index 73aba16b2f97..4fce98e31361 100644 --- a/usr.bin/tip/tip/value.c +++ b/usr.bin/tip/tip/value.c @@ -1,359 +1,355 @@ /* $OpenBSD: value.c,v 1.14 2006/03/17 22:02:58 moritz Exp $ */ /* $NetBSD: value.c,v 1.6 1997/02/11 09:24:09 mrg Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - #include "tip.h" #define MIDDLE 35 static value_t *vlookup(char *); static void vassign(value_t *, char *); static void vtoken(char *); static void vprint(value_t *); static int vaccess(unsigned int, unsigned int); static char *vinterp(char *, int); static size_t col = 0; /* * Variable manipulation */ void vinit(void) { char file[FILENAME_MAX], *cp; value_t *p; FILE *fp; for (p = vtable; p->v_name != NULL; p++) { if (p->v_type&ENVIRON) if ((cp = getenv(p->v_name))) p->v_value = cp; if (p->v_type&IREMOTE) { switch (p->v_type&TMASK) { case STRING: p->v_value = *(char **)p->v_value; break; case NUMBER: setnumber(p->v_value, *(long *)p->v_value); break; case BOOL: setboolean(p->v_value, *(short *)p->v_value); break; case CHAR: setcharacter(p->v_value, *(char *)p->v_value); break; } } } /* * Read the .tiprc file in the HOME directory * for sets */ cp = value(HOME); if (cp == NULL) { (void)fprintf(stderr, "$HOME not set. Skipping check for ~/.tiprc\n"); } else if (strlen(cp) + sizeof("/.tiprc") > sizeof(file)) { (void)fprintf(stderr, "Home directory path too long: %s\n", value(HOME)); } else { snprintf(file, sizeof file, "%s/.tiprc", value(HOME)); if ((fp = fopen(file, "r")) != NULL) { char *tp; while (fgets(file, sizeof(file)-1, fp) != NULL) { if (vflag) printf("set %s", file); if ((tp = strrchr(file, '\n'))) *tp = '\0'; vlex(file); } fclose(fp); } } /* * To allow definition of exception prior to fork */ vtable[EXCEPTIONS].v_access &= ~(WRITE<v_access, WRITE)) { printf("access denied\r\n"); return; } switch (p->v_type&TMASK) { case STRING: if (p->v_value && equal(p->v_value, v)) return; if (!(p->v_type&(ENVIRON|INIT))) free(p->v_value); if ((p->v_value = strdup(v)) == NOSTR) { printf("out of core\r\n"); return; } p->v_type &= ~(ENVIRON|INIT); break; case NUMBER: if (number(p->v_value) == number(v)) return; setnumber(p->v_value, number(v)); break; case BOOL: if (boolean(p->v_value) == (*v != '!')) return; setboolean(p->v_value, (*v != '!')); break; case CHAR: if (character(p->v_value) == *v) return; setcharacter(p->v_value, *v); } p->v_access |= CHANGED; } void vlex(char *s) { value_t *p; char *cp; if (equal(s, "all")) { for (p = vtable; p->v_name; p++) if (vaccess(p->v_access, READ)) vprint(p); } else { do { if ((cp = vinterp(s, ' '))) cp++; vtoken(s); s = cp; } while (s); } if (col > 0) { printf("\r\n"); col = 0; } } static void vtoken(char *s) { value_t *p; char *cp; if ((cp = strchr(s, '='))) { *cp = '\0'; if ((p = vlookup(s))) { cp++; if (p->v_type&NUMBER) vassign(p, (char *)(intptr_t)atoi(cp)); else { if (strcmp(s, "record") == 0) cp = expand(cp); vassign(p, cp); } return; } } else if ((cp = strchr(s, '?'))) { *cp = '\0'; if ((p = vlookup(s)) && vaccess(p->v_access, READ)) { vprint(p); return; } } else { if (*s != '!') p = vlookup(s); else p = vlookup(s+1); if (p != NOVAL) { vassign(p, s); return; } } printf("%s: unknown variable\r\n", s); } static void vprint(value_t *p) { char *cp; if (col > 0 && col < MIDDLE) while (col++ < MIDDLE) putchar(' '); col += size(p->v_name); switch (p->v_type&TMASK) { case BOOL: if (boolean(p->v_value) == FALSE) { col++; putchar('!'); } printf("%s", p->v_name); break; case STRING: printf("%s=", p->v_name); col++; if (p->v_value) { cp = interp(p->v_value); col += size(cp); printf("%s", cp); } break; case NUMBER: col += 6; printf("%s=%-5ld", p->v_name, number(p->v_value)); break; case CHAR: printf("%s=", p->v_name); col++; if (p->v_value) { cp = ctrl(character(p->v_value)); col += size(cp); printf("%s", cp); } break; } if (col >= MIDDLE) { col = 0; printf("\r\n"); return; } } static int vaccess(unsigned int mode, unsigned int rw) { if (mode & (rw<v_name; p++) if (equal(p->v_name, s) || (p->v_abrev && equal(p->v_abrev, s))) return (p); return (NULL); } static char * vinterp(char *s, int stop) { char *p = s, c; int num; while ((c = *s++) && c != stop) { switch (c) { case '^': if (*s) *p++ = *s++ - 0100; else *p++ = c; break; case '\\': num = 0; c = *s++; if (c >= '0' && c <= '7') num = (num<<3)+(c-'0'); else { char *q = "n\nr\rt\tb\bf\f"; for (; *q; q++) if (c == *q++) { *p++ = *q; goto cont; } *p++ = c; cont: break; } if ((c = *s++) >= '0' && c <= '7') { num = (num<<3)+(c-'0'); if ((c = *s++) >= '0' && c <= '7') num = (num<<3)+(c-'0'); else s--; } else s--; *p++ = num; break; default: *p++ = c; } } *p = '\0'; return (c == stop ? s-1 : NULL); } /* * assign variable s with value v (for NUMBER or STRING or CHAR types) */ int vstring(char *s, char *v) { value_t *p; p = vlookup(s); if (p == 0) return (1); if (p->v_type&NUMBER) vassign(p, (char *)(intptr_t)atoi(v)); else { if (strcmp(s, "record") == 0) v = expand(v); vassign(p, v); } return (0); } diff --git a/usr.bin/tip/tip/vars.c b/usr.bin/tip/tip/vars.c index 4741b7413af9..332d6b871ff1 100644 --- a/usr.bin/tip/tip/vars.c +++ b/usr.bin/tip/tip/vars.c @@ -1,120 +1,116 @@ /* $OpenBSD: vars.c,v 1.8 2006/08/18 03:06:18 jason Exp $ */ /* $NetBSD: vars.c,v 1.3 1994/12/08 09:31:19 jtc Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include -#ifndef lint -#endif /* not lint */ - #include "tip.h" #include "pathnames.h" /* * Definition of variables */ value_t vtable[] = { { "beautify", BOOL, (READ|WRITE)<f <file < * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include using namespace std; int main(int argc, char **) { struct utmpx *ut; set names; if (argc > 1) { cerr << "usage: users" << endl; return (1); } setutxent(); if (caph_enter()) err(1, "Failed to enter capability mode."); while ((ut = getutxent()) != NULL) if (ut->ut_type == USER_PROCESS) names.insert(ut->ut_user); endutxent(); if (!names.empty()) { set::iterator last = names.end(); --last; copy(names.begin(), last, ostream_iterator(cout, " ")); cout << *last << endl; } } diff --git a/usr.bin/vtfontcvt/vtfontcvt.c b/usr.bin/vtfontcvt/vtfontcvt.c index d68516dda360..ddff1580a993 100644 --- a/usr.bin/vtfontcvt/vtfontcvt.c +++ b/usr.bin/vtfontcvt/vtfontcvt.c @@ -1,1121 +1,1120 @@ /*- * Copyright (c) 2009, 2014 The FreeBSD Foundation * * This software was developed by Ed Schouten under sponsorship from the * FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define VFNT_MAXGLYPHS 131072 #define VFNT_MAXDIMENSION 128 static unsigned int width = 8, wbytes, height = 16; struct glyph { TAILQ_ENTRY(glyph) g_list; SLIST_ENTRY(glyph) g_hash; uint8_t *g_data; unsigned int g_index; }; #define FONTCVT_NHASH 4096 TAILQ_HEAD(glyph_list, glyph); static SLIST_HEAD(, glyph) glyph_hash[FONTCVT_NHASH]; static struct glyph_list glyphs[VFNT_MAPS] = { TAILQ_HEAD_INITIALIZER(glyphs[0]), TAILQ_HEAD_INITIALIZER(glyphs[1]), TAILQ_HEAD_INITIALIZER(glyphs[2]), TAILQ_HEAD_INITIALIZER(glyphs[3]), }; static unsigned int glyph_total, glyph_count[4], glyph_unique, glyph_dupe; struct mapping { TAILQ_ENTRY(mapping) m_list; unsigned int m_char; unsigned int m_length; struct glyph *m_glyph; }; TAILQ_HEAD(mapping_list, mapping); static struct mapping_list maps[VFNT_MAPS] = { TAILQ_HEAD_INITIALIZER(maps[0]), TAILQ_HEAD_INITIALIZER(maps[1]), TAILQ_HEAD_INITIALIZER(maps[2]), TAILQ_HEAD_INITIALIZER(maps[3]), }; static unsigned int mapping_total, map_count[4], map_folded_count[4], mapping_unique, mapping_dupe; enum output_format { VT_FONT, /* default */ VT_C_SOURCE, /* C source for built in fonts */ VT_C_COMPRESSED /* C source with compressed font data */ }; struct whitelist { uint32_t c; uint32_t len; }; /* * Compressed font glyph list. To be used with boot loader, we need to have * ascii set and box drawing chars. */ static struct whitelist c_list[] = { { .c = 0, .len = 0 }, /* deault char */ { .c = 0x20, .len = 0x5f }, { .c = 0x2500, .len = 0 }, /* single frame */ { .c = 0x2502, .len = 0 }, { .c = 0x250c, .len = 0 }, { .c = 0x2510, .len = 0 }, { .c = 0x2514, .len = 0 }, { .c = 0x2518, .len = 0 }, { .c = 0x2550, .len = 1 }, /* double frame */ { .c = 0x2554, .len = 0 }, { .c = 0x2557, .len = 0 }, { .c = 0x255a, .len = 0 }, { .c = 0x255d, .len = 0 }, }; /* * Uncompressed source. For x86 we need cp437 so the vga text mode * can program font into the vga card. */ static struct whitelist s_list[] = { { .c = 0, .len = 0 }, /* deault char */ { .c = 0x20, .len = 0x5f }, /* ascii set */ { .c = 0xA0, .len = 0x5f }, /* latin 1 */ { .c = 0x0192, .len = 0 }, { .c = 0x0332, .len = 0 }, /* composing lower line */ { .c = 0x0393, .len = 0 }, { .c = 0x0398, .len = 0 }, { .c = 0x03A3, .len = 0 }, { .c = 0x03A6, .len = 0 }, { .c = 0x03A9, .len = 0 }, { .c = 0x03B1, .len = 1 }, { .c = 0x03B4, .len = 0 }, { .c = 0x03C0, .len = 0 }, { .c = 0x03C3, .len = 0 }, { .c = 0x03C4, .len = 0 }, { .c = 0x207F, .len = 0 }, { .c = 0x20A7, .len = 0 }, { .c = 0x2205, .len = 0 }, { .c = 0x220A, .len = 0 }, { .c = 0x2219, .len = 1 }, { .c = 0x221E, .len = 0 }, { .c = 0x2229, .len = 0 }, { .c = 0x2248, .len = 0 }, { .c = 0x2261, .len = 0 }, { .c = 0x2264, .len = 1 }, { .c = 0x2310, .len = 0 }, { .c = 0x2320, .len = 1 }, { .c = 0x2500, .len = 0 }, { .c = 0x2502, .len = 0 }, { .c = 0x250C, .len = 0 }, { .c = 0x2510, .len = 0 }, { .c = 0x2514, .len = 0 }, { .c = 0x2518, .len = 0 }, { .c = 0x251C, .len = 0 }, { .c = 0x2524, .len = 0 }, { .c = 0x252C, .len = 0 }, { .c = 0x2534, .len = 0 }, { .c = 0x253C, .len = 0 }, { .c = 0x2550, .len = 0x1c }, { .c = 0x2580, .len = 0 }, { .c = 0x2584, .len = 0 }, { .c = 0x2588, .len = 0 }, { .c = 0x258C, .len = 0 }, { .c = 0x2590, .len = 3 }, { .c = 0x25A0, .len = 0 }, }; static bool filter = true; static enum output_format format = VT_FONT; /* Type for write callback. */ typedef size_t (*vt_write)(const void *, size_t, size_t, FILE *); static uint8_t *uncompressed; static void usage(void) { (void)fprintf(stderr, "usage: vtfontcvt " "[-nv] [-f format] [-h height] [-w width]\n" "\t-o output_file normal_font [bold_font]\n"); exit(1); } static void * xmalloc(size_t size) { void *m; if ((m = calloc(1, size)) == NULL) errx(1, "memory allocation failure"); return (m); } static int add_mapping(struct glyph *gl, unsigned int c, unsigned int map_idx) { struct mapping *mp, *mp_temp; struct mapping_list *ml; mapping_total++; mp = xmalloc(sizeof *mp); mp->m_char = c; mp->m_glyph = gl; mp->m_length = 0; ml = &maps[map_idx]; if (TAILQ_LAST(ml, mapping_list) == NULL || TAILQ_LAST(ml, mapping_list)->m_char < c) { /* Common case: empty list or new char at end of list. */ TAILQ_INSERT_TAIL(ml, mp, m_list); } else { /* Find insertion point for char; cannot be at end. */ TAILQ_FOREACH(mp_temp, ml, m_list) { if (mp_temp->m_char >= c) { TAILQ_INSERT_BEFORE(mp_temp, mp, m_list); break; } } } map_count[map_idx]++; mapping_unique++; return (0); } static int dedup_mapping(unsigned int map_idx) { struct mapping *mp_bold, *mp_normal, *mp_temp; unsigned normal_map_idx = map_idx - VFNT_MAP_BOLD; assert(map_idx == VFNT_MAP_BOLD || map_idx == VFNT_MAP_BOLD_RIGHT); mp_normal = TAILQ_FIRST(&maps[normal_map_idx]); TAILQ_FOREACH_SAFE(mp_bold, &maps[map_idx], m_list, mp_temp) { while (mp_normal->m_char < mp_bold->m_char) mp_normal = TAILQ_NEXT(mp_normal, m_list); if (mp_bold->m_char != mp_normal->m_char) errx(1, "Character %u not in normal font!", mp_bold->m_char); if (mp_bold->m_glyph != mp_normal->m_glyph) continue; /* No mapping is needed if it's equal to the normal mapping. */ TAILQ_REMOVE(&maps[map_idx], mp_bold, m_list); free(mp_bold); mapping_dupe++; } return (0); } static struct glyph * add_glyph(const uint8_t *bytes, unsigned int map_idx, int fallback) { struct glyph *gl; int hash; glyph_total++; glyph_count[map_idx]++; /* Return existing glyph if we have an identical one. */ hash = fnv_32_buf(bytes, wbytes * height, FNV1_32_INIT) % FONTCVT_NHASH; SLIST_FOREACH(gl, &glyph_hash[hash], g_hash) { if (memcmp(gl->g_data, bytes, wbytes * height) == 0) { glyph_dupe++; return (gl); } } /* Allocate new glyph. */ gl = xmalloc(sizeof *gl); gl->g_data = xmalloc(wbytes * height); memcpy(gl->g_data, bytes, wbytes * height); if (fallback) TAILQ_INSERT_HEAD(&glyphs[map_idx], gl, g_list); else TAILQ_INSERT_TAIL(&glyphs[map_idx], gl, g_list); SLIST_INSERT_HEAD(&glyph_hash[hash], gl, g_hash); glyph_unique++; if (glyph_unique > VFNT_MAXGLYPHS) errx(1, "too many glyphs (%u)", glyph_unique); return (gl); } static bool check_whitelist(unsigned c) { struct whitelist *w = NULL; int i, n = 0; if (filter == false) return (true); if (format == VT_C_SOURCE) { w = s_list; n = sizeof(s_list) / sizeof(s_list[0]); } if (format == VT_C_COMPRESSED) { w = c_list; n = sizeof(c_list) / sizeof(c_list[0]); } if (w == NULL) return (true); for (i = 0; i < n; i++) { if (c >= w[i].c && c <= w[i].c + w[i].len) return (true); } return (false); } static int add_char(unsigned curchar, unsigned map_idx, uint8_t *bytes, uint8_t *bytes_r) { struct glyph *gl; /* Prevent adding two glyphs for 0xFFFD */ if (curchar == 0xFFFD) { if (map_idx < VFNT_MAP_BOLD) gl = add_glyph(bytes, 0, 1); } else if (filter == false || curchar >= 0x20) { gl = add_glyph(bytes, map_idx, 0); if (add_mapping(gl, curchar, map_idx) != 0) return (1); if (bytes_r != NULL) { gl = add_glyph(bytes_r, map_idx + 1, 0); if (add_mapping(gl, curchar, map_idx + 1) != 0) return (1); } } return (0); } /* * Right-shift glyph row. */ static void rshift_row(uint8_t *buf, size_t len, size_t shift) { ssize_t i, off_byte = shift / 8; size_t off_bit = shift % 8; if (shift == 0) return; for (i = len - 1; i >= 0; i--) buf[i] = (i >= off_byte ? buf[i - off_byte] >> off_bit : 0) | (i > off_byte ? buf[i - off_byte - 1] << (8 - off_bit) : 0); } /* * Split double-width characters into left and right half. Single-width * characters in _left_ only. */ static int split_row(uint8_t *left, uint8_t *right, uint8_t *line, size_t w) { size_t s, i; s = wbytes * 8 - width; memcpy(left, line, wbytes); *(left + wbytes - 1) &= 0xFF << s; if (w > width) { /* Double-width character. */ uint8_t t; for (i = 0; i < wbytes; i++) { t = *(line + wbytes + i - 1); t <<= 8 - s; t |= *(line + wbytes + i) >> s; *(right + i) = t; } *(right + wbytes - 1) &= 0xFF << s; } return (0); } static void set_height(int h) { if (h <= 0 || h > VFNT_MAXDIMENSION) errx(1, "invalid height %d", h); height = h; } static void set_width(int w) { if (w <= 0 || w > VFNT_MAXDIMENSION) errx(1, "invalid width %d", w); width = w; wbytes = howmany(width, 8); } static int parse_bdf(FILE *fp, unsigned int map_idx) { char *ln, *p; size_t length; uint8_t *line, *bytes, *bytes_r; unsigned int curchar = 0, i, j, linenum = 0, bbwbytes; int bbw, bbh, bbox, bboy; /* Glyph bounding box. */ int fbbw = 0, fbbh, fbbox, fbboy; /* Font bounding box. */ int dwidth = 0, dwy = 0; int rv = -1; char spc = '\0'; /* * Step 1: Parse FONT logical font descriptor and FONTBOUNDINGBOX * bounding box. */ while ((ln = fgetln(fp, &length)) != NULL) { linenum++; ln[length - 1] = '\0'; if (strncmp(ln, "FONT ", 5) == 0) { p = ln + 5; i = 0; while ((p = strchr(p, '-')) != NULL) { p++; i++; if (i == 11) { spc = *p; break; } } } else if (strncmp(ln, "FONTBOUNDINGBOX ", 16) == 0) { if (sscanf(ln + 16, "%d %d %d %d", &fbbw, &fbbh, &fbbox, &fbboy) != 4) errx(1, "invalid FONTBOUNDINGBOX at line %u", linenum); set_width(fbbw); set_height(fbbh); break; } } if (fbbw == 0) errx(1, "broken font header"); if (spc != 'c' && spc != 'C') errx(1, "font spacing \"C\" (character cell) required"); /* Step 2: Validate DWIDTH (Device Width) of all glyphs. */ while ((ln = fgetln(fp, &length)) != NULL) { linenum++; ln[length - 1] = '\0'; if (strncmp(ln, "DWIDTH ", 7) == 0) { if (sscanf(ln + 7, "%d %d", &dwidth, &dwy) != 2) errx(1, "invalid DWIDTH at line %u", linenum); if (dwy != 0 || (dwidth != fbbw && dwidth * 2 != fbbw)) errx(1, "bitmap with unsupported DWIDTH %d %d at line %u", dwidth, dwy, linenum); if (dwidth < fbbw) set_width(dwidth); } } /* Step 3: Restart at the beginning of the file and read glyph data. */ dwidth = bbw = bbh = 0; rewind(fp); linenum = 0; bbwbytes = 0; /* GCC 4.2.1 "may be used uninitialized" workaround. */ bytes = xmalloc(wbytes * height); bytes_r = xmalloc(wbytes * height); line = xmalloc(wbytes * 2); while ((ln = fgetln(fp, &length)) != NULL) { linenum++; ln[length - 1] = '\0'; if (strncmp(ln, "ENCODING ", 9) == 0) { curchar = atoi(ln + 9); } else if (strncmp(ln, "DWIDTH ", 7) == 0) { dwidth = atoi(ln + 7); } else if (strncmp(ln, "BBX ", 4) == 0) { if (sscanf(ln + 4, "%d %d %d %d", &bbw, &bbh, &bbox, &bboy) != 4) errx(1, "invalid BBX at line %u", linenum); if (bbw < 1 || bbh < 1 || bbw > fbbw || bbh > fbbh || bbox < fbbox || bboy < fbboy || bbh + bboy > fbbh + fbboy) errx(1, "broken bitmap with BBX %d %d %d %d at line %u", bbw, bbh, bbox, bboy, linenum); bbwbytes = howmany(bbw, 8); } else if (strncmp(ln, "BITMAP", 6) == 0 && (ln[6] == ' ' || ln[6] == '\0')) { if (dwidth == 0 || bbw == 0 || bbh == 0) errx(1, "broken char header at line %u!", linenum); memset(bytes, 0, wbytes * height); memset(bytes_r, 0, wbytes * height); /* * Assume that the next _bbh_ lines are bitmap data. * ENDCHAR is allowed to terminate the bitmap * early but is not otherwise checked; any extra data * is ignored. */ for (i = (fbbh + fbboy) - (bbh + bboy); i < (unsigned int)((fbbh + fbboy) - bboy); i++) { if ((ln = fgetln(fp, &length)) == NULL) errx(1, "unexpected EOF"); linenum++; ln[length - 1] = '\0'; if (strcmp(ln, "ENDCHAR") == 0) break; if (strlen(ln) < bbwbytes * 2) errx(1, "broken bitmap at line %u", linenum); memset(line, 0, wbytes * 2); for (j = 0; j < bbwbytes; j++) { unsigned int val; if (sscanf(ln + j * 2, "%2x", &val) == 0) break; *(line + j) = (uint8_t)val; } rshift_row(line, wbytes * 2, bbox - fbbox); rv = split_row(bytes + i * wbytes, bytes_r + i * wbytes, line, dwidth); if (rv != 0) goto out; } if (check_whitelist(curchar) == true) { rv = add_char(curchar, map_idx, bytes, dwidth > (int)width ? bytes_r : NULL); if (rv != 0) goto out; } dwidth = bbw = bbh = 0; } } out: free(bytes); free(bytes_r); free(line); return (rv); } static int parse_hex(FILE *fp, unsigned int map_idx) { char *ln, *p; size_t length; uint8_t *bytes = NULL, *bytes_r = NULL, *line = NULL; unsigned curchar = 0, gwidth, gwbytes, i, j, chars_per_row; int rv = 0; while ((ln = fgetln(fp, &length)) != NULL) { ln[length - 1] = '\0'; if (strncmp(ln, "# Height: ", 10) == 0) { if (bytes != NULL) errx(1, "malformed input: Height tag after font data"); set_height(atoi(ln + 10)); } else if (strncmp(ln, "# Width: ", 9) == 0) { if (bytes != NULL) errx(1, "malformed input: Width tag after font data"); set_width(atoi(ln + 9)); } else if (sscanf(ln, "%6x:", &curchar) == 1) { if (bytes == NULL) { bytes = xmalloc(wbytes * height); bytes_r = xmalloc(wbytes * height); line = xmalloc(wbytes * 2); } /* ln is guaranteed to have a colon here. */ p = strchr(ln, ':') + 1; chars_per_row = strlen(p) / height; if (chars_per_row < wbytes * 2) errx(1, "malformed input: broken bitmap, character %06x", curchar); gwidth = width * 2; gwbytes = howmany(gwidth, 8); if (chars_per_row < gwbytes * 2 || gwidth <= 8) { gwidth = width; /* Single-width character. */ gwbytes = wbytes; } for (i = 0; i < height; i++) { for (j = 0; j < gwbytes; j++) { unsigned int val; if (sscanf(p + j * 2, "%2x", &val) == 0) break; *(line + j) = (uint8_t)val; } rv = split_row(bytes + i * wbytes, bytes_r + i * wbytes, line, gwidth); if (rv != 0) goto out; p += gwbytes * 2; } if (check_whitelist(curchar) == true) { rv = add_char(curchar, map_idx, bytes, gwidth != width ? bytes_r : NULL); if (rv != 0) goto out; } } } out: free(bytes); free(bytes_r); free(line); return (rv); } static int parse_file(const char *filename, unsigned int map_idx) { FILE *fp; size_t len; int rv; fp = fopen(filename, "r"); if (fp == NULL) { perror(filename); return (1); } len = strlen(filename); if (len > 4 && strcasecmp(filename + len - 4, ".hex") == 0) rv = parse_hex(fp, map_idx); else rv = parse_bdf(fp, map_idx); fclose(fp); return (rv); } static void number_glyphs(void) { struct glyph *gl; unsigned int i, idx = 0; for (i = 0; i < VFNT_MAPS; i++) TAILQ_FOREACH(gl, &glyphs[i], g_list) gl->g_index = idx++; } /* Note we only deal with byte stream here. */ static size_t write_glyph_source(const void *ptr, size_t size, size_t nitems, FILE *stream) { const uint8_t *data = ptr; size_t i; size *= nitems; for (i = 0; i < size; i++) { if ((i % wbytes) == 0) { if (fprintf(stream, "\n") < 0) return (0); } if (fprintf(stream, "0x%02x, ", data[i]) < 0) return (0); } if (fprintf(stream, "\n") < 0) nitems = 0; return (nitems); } /* Write to buffer */ static size_t write_glyph_buf(const void *ptr, size_t size, size_t nitems, FILE *stream __unused) { static size_t index = 0; size *= nitems; (void) memmove(uncompressed + index, ptr, size); index += size; return (nitems); } static int write_glyphs(FILE *fp, vt_write cb) { struct glyph *gl; unsigned int i; for (i = 0; i < VFNT_MAPS; i++) { TAILQ_FOREACH(gl, &glyphs[i], g_list) if (cb(gl->g_data, wbytes * height, 1, fp) != 1) return (1); } return (0); } static void fold_mappings(unsigned int map_idx) { struct mapping_list *ml = &maps[map_idx]; struct mapping *mn, *mp, *mbase; mp = mbase = TAILQ_FIRST(ml); for (mp = mbase = TAILQ_FIRST(ml); mp != NULL; mp = mn) { mn = TAILQ_NEXT(mp, m_list); if (mn != NULL && mn->m_char == mp->m_char + 1 && mn->m_glyph->g_index == mp->m_glyph->g_index + 1) continue; mbase->m_length = mp->m_char - mbase->m_char + 1; mbase = mp = mn; map_folded_count[map_idx]++; } } static int write_mappings(FILE *fp, unsigned int map_idx) { struct mapping_list *ml = &maps[map_idx]; struct mapping *mp; vfnt_map_t fm; unsigned int i = 0, j = 0; TAILQ_FOREACH(mp, ml, m_list) { j++; if (mp->m_length > 0) { i += mp->m_length; fm.vfm_src = htobe32(mp->m_char); fm.vfm_dst = htobe16(mp->m_glyph->g_index); fm.vfm_len = htobe16(mp->m_length - 1); if (fwrite(&fm, sizeof fm, 1, fp) != 1) return (1); } } assert(i == j); return (0); } static int write_source_mappings(FILE *fp, unsigned int map_idx) { struct mapping_list *ml = &maps[map_idx]; struct mapping *mp; unsigned int i = 0, j = 0; TAILQ_FOREACH(mp, ml, m_list) { j++; if (mp->m_length > 0) { i += mp->m_length; if (fprintf(fp, "\t{ 0x%08x, 0x%04x, 0x%04x },\n", mp->m_char, mp->m_glyph->g_index, mp->m_length - 1) < 0) return (1); } } assert(i == j); return (0); } static int write_fnt(const char *filename) { FILE *fp; struct font_header fh = { .fh_magic = FONT_HEADER_MAGIC, }; fp = fopen(filename, "wb"); if (fp == NULL) { perror(filename); return (1); } fh.fh_width = width; fh.fh_height = height; fh.fh_glyph_count = htobe32(glyph_unique); fh.fh_map_count[0] = htobe32(map_folded_count[0]); fh.fh_map_count[1] = htobe32(map_folded_count[1]); fh.fh_map_count[2] = htobe32(map_folded_count[2]); fh.fh_map_count[3] = htobe32(map_folded_count[3]); if (fwrite(&fh, sizeof(fh), 1, fp) != 1) { perror(filename); fclose(fp); return (1); } if (write_glyphs(fp, &fwrite) != 0 || write_mappings(fp, VFNT_MAP_NORMAL) != 0 || write_mappings(fp, VFNT_MAP_NORMAL_RIGHT) != 0 || write_mappings(fp, VFNT_MAP_BOLD) != 0 || write_mappings(fp, VFNT_MAP_BOLD_RIGHT) != 0) { perror(filename); fclose(fp); return (1); } fclose(fp); return (0); } static int write_fnt_source(bool lz4, const char *filename) { FILE *fp; int rv = 1; size_t uncompressed_size = wbytes * height * glyph_unique; size_t compressed_size = uncompressed_size; uint8_t *compressed = NULL; fp = fopen(filename, "w"); if (fp == NULL) { perror(filename); return (1); } if (lz4 == true) { uncompressed = xmalloc(uncompressed_size); compressed = xmalloc(uncompressed_size); } if (fprintf(fp, "/* Generated %ux%u console font source. */\n\n", width, height) < 0) goto done; if (fprintf(fp, "#include \n") < 0) goto done; if (fprintf(fp, "#include \n") < 0) goto done; if (fprintf(fp, "#include \n\n") < 0) goto done; /* Write font bytes. */ if (fprintf(fp, "static uint8_t FONTDATA_%ux%u[] = {\n", width, height) < 0) goto done; if (lz4 == true) { if (write_glyphs(fp, &write_glyph_buf) != 0) goto done; compressed_size = lz4_compress(uncompressed, compressed, uncompressed_size, compressed_size, 0); if (write_glyph_source(compressed, compressed_size, 1, fp) != 1) goto done; free(uncompressed); free(compressed); } else { if (write_glyphs(fp, &write_glyph_source) != 0) goto done; } if (fprintf(fp, "};\n\n") < 0) goto done; /* Write font maps. */ if (!TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL])) { if (fprintf(fp, "static vfnt_map_t " "FONTMAP_NORMAL_%ux%u[] = {\n", width, height) < 0) goto done; if (write_source_mappings(fp, VFNT_MAP_NORMAL) != 0) goto done; if (fprintf(fp, "};\n\n") < 0) goto done; } if (!TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL_RIGHT])) { if (fprintf(fp, "static vfnt_map_t " "FONTMAP_NORMAL_RH_%ux%u[] = {\n", width, height) < 0) goto done; if (write_source_mappings(fp, VFNT_MAP_NORMAL_RIGHT) != 0) goto done; if (fprintf(fp, "};\n\n") < 0) goto done; } if (!TAILQ_EMPTY(&maps[VFNT_MAP_BOLD])) { if (fprintf(fp, "static vfnt_map_t " "FONTMAP_BOLD_%ux%u[] = {\n", width, height) < 0) goto done; if (write_source_mappings(fp, VFNT_MAP_BOLD) != 0) goto done; if (fprintf(fp, "};\n\n") < 0) goto done; } if (!TAILQ_EMPTY(&maps[VFNT_MAP_BOLD_RIGHT])) { if (fprintf(fp, "static vfnt_map_t " "FONTMAP_BOLD_RH_%ux%u[] = {\n", width, height) < 0) goto done; if (write_source_mappings(fp, VFNT_MAP_BOLD_RIGHT) != 0) goto done; if (fprintf(fp, "};\n\n") < 0) goto done; } /* Write struct font. */ if (fprintf(fp, "struct vt_font font_%ux%u = {\n", width, height) < 0) goto done; if (fprintf(fp, "\t.vf_map\t= {\n") < 0) goto done; if (TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL])) { if (fprintf(fp, "\t\t\tNULL,\n") < 0) goto done; } else { if (fprintf(fp, "\t\t\tFONTMAP_NORMAL_%ux%u,\n", width, height) < 0) goto done; } if (TAILQ_EMPTY(&maps[VFNT_MAP_NORMAL_RIGHT])) { if (fprintf(fp, "\t\t\tNULL,\n") < 0) goto done; } else { if (fprintf(fp, "\t\t\tFONTMAP_NORMAL_RH_%ux%u,\n", width, height) < 0) goto done; } if (TAILQ_EMPTY(&maps[VFNT_MAP_BOLD])) { if (fprintf(fp, "\t\t\tNULL,\n") < 0) goto done; } else { if (fprintf(fp, "\t\t\tFONTMAP_BOLD_%ux%u,\n", width, height) < 0) goto done; } if (TAILQ_EMPTY(&maps[VFNT_MAP_BOLD_RIGHT])) { if (fprintf(fp, "\t\t\tNULL\n") < 0) goto done; } else { if (fprintf(fp, "\t\t\tFONTMAP_BOLD_RH_%ux%u\n", width, height) < 0) goto done; } if (fprintf(fp, "\t\t},\n") < 0) goto done; if (lz4 == true) { if (fprintf(fp, "\t.vf_bytes\t= NULL,\n") < 0) goto done; } else { if (fprintf(fp, "\t.vf_bytes\t= FONTDATA_%ux%u,\n", width, height) < 0) { goto done; } } if (fprintf(fp, "\t.vf_width\t= %u,\n", width) < 0) goto done; if (fprintf(fp, "\t.vf_height\t= %u,\n", height) < 0) goto done; if (fprintf(fp, "\t.vf_map_count\t= { %u, %u, %u, %u }\n", map_folded_count[0], map_folded_count[1], map_folded_count[2], map_folded_count[3]) < 0) { goto done; } if (fprintf(fp, "};\n\n") < 0) goto done; /* Write bitmap data. */ if (fprintf(fp, "vt_font_bitmap_data_t font_data_%ux%u = {\n", width, height) < 0) goto done; if (fprintf(fp, "\t.vfbd_width\t= %u,\n", width) < 0) goto done; if (fprintf(fp, "\t.vfbd_height\t= %u,\n", height) < 0) goto done; if (lz4 == true) { if (fprintf(fp, "\t.vfbd_compressed_size\t= %zu,\n", compressed_size) < 0) { goto done; } if (fprintf(fp, "\t.vfbd_uncompressed_size\t= %zu,\n", uncompressed_size) < 0) { goto done; } if (fprintf(fp, "\t.vfbd_compressed_data\t= FONTDATA_%ux%u,\n", width, height) < 0) { goto done; } } else { if (fprintf(fp, "\t.vfbd_compressed_size\t= 0,\n") < 0) goto done; if (fprintf(fp, "\t.vfbd_uncompressed_size\t= %zu,\n", uncompressed_size) < 0) { goto done; } if (fprintf(fp, "\t.vfbd_compressed_data\t= NULL,\n") < 0) goto done; } if (fprintf(fp, "\t.vfbd_font = &font_%ux%u\n", width, height) < 0) goto done; if (fprintf(fp, "};\n") < 0) goto done; rv = 0; done: if (rv != 0) perror(filename); fclose(fp); return (0); } static void print_font_info(void) { printf( "Statistics:\n" "- width: %6u\n" "- height: %6u\n" "- glyph_total: %6u\n" "- glyph_normal: %6u\n" "- glyph_normal_right: %6u\n" "- glyph_bold: %6u\n" "- glyph_bold_right: %6u\n" "- glyph_unique: %6u\n" "- glyph_dupe: %6u\n" "- mapping_total: %6u\n" "- mapping_normal: %6u\n" "- mapping_normal_folded: %6u\n" "- mapping_normal_right: %6u\n" "- mapping_normal_right_folded: %6u\n" "- mapping_bold: %6u\n" "- mapping_bold_folded: %6u\n" "- mapping_bold_right: %6u\n" "- mapping_bold_right_folded: %6u\n" "- mapping_unique: %6u\n" "- mapping_dupe: %6u\n", width, height, glyph_total, glyph_count[0], glyph_count[1], glyph_count[2], glyph_count[3], glyph_unique, glyph_dupe, mapping_total, map_count[0], map_folded_count[0], map_count[1], map_folded_count[1], map_count[2], map_folded_count[2], map_count[3], map_folded_count[3], mapping_unique, mapping_dupe); } int main(int argc, char *argv[]) { int ch, verbose = 0, rv = 0; char *outfile = NULL; assert(sizeof(struct font_header) == 32); assert(sizeof(vfnt_map_t) == 8); while ((ch = getopt(argc, argv, "nf:h:vw:o:")) != -1) { switch (ch) { case 'f': if (strcmp(optarg, "font") == 0) format = VT_FONT; else if (strcmp(optarg, "source") == 0) format = VT_C_SOURCE; else if (strcmp(optarg, "compressed-source") == 0) format = VT_C_COMPRESSED; else errx(1, "Invalid format: %s", optarg); break; case 'h': height = atoi(optarg); break; case 'n': filter = false; break; case 'o': outfile = optarg; break; case 'v': verbose = 1; break; case 'w': width = atoi(optarg); break; case '?': default: usage(); } } argc -= optind; argv += optind; if (outfile == NULL && (argc < 2 || argc > 3)) usage(); if (outfile == NULL) { outfile = argv[argc - 1]; argc--; } set_width(width); set_height(height); if (parse_file(argv[0], VFNT_MAP_NORMAL) != 0) return (1); argc--; argv++; if (argc == 1) { if (parse_file(argv[0], VFNT_MAP_BOLD) != 0) return (1); argc--; argv++; } number_glyphs(); dedup_mapping(VFNT_MAP_BOLD); dedup_mapping(VFNT_MAP_BOLD_RIGHT); fold_mappings(0); fold_mappings(1); fold_mappings(2); fold_mappings(3); switch (format) { case VT_FONT: rv = write_fnt(outfile); break; case VT_C_SOURCE: rv = write_fnt_source(false, outfile); break; case VT_C_COMPRESSED: rv = write_fnt_source(true, outfile); break; } if (verbose) print_font_info(); return (rv); } diff --git a/usr.bin/w/proc_compare.c b/usr.bin/w/proc_compare.c index c19c65f6229c..36a63d31cc4a 100644 --- a/usr.bin/w/proc_compare.c +++ b/usr.bin/w/proc_compare.c @@ -1,118 +1,114 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 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. 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 -#endif - -#include #include #include #include #include #include "extern.h" /* * Returns 1 if p2 is "better" than p1 * * The algorithm for picking the "interesting" process is thus: * * 1) Only foreground processes are eligible - implied. * 2) Runnable processes are favored over anything else. The runner * with the highest cpu utilization is picked (p_estcpu). Ties are * broken by picking the highest pid. * 3) The sleeper with the shortest sleep time is next. With ties, * we pick out just "short-term" sleepers (TDF_SINTR == 0). * 4) Further ties are broken by picking the highest pid. * * If you change this, be sure to consider making the change in the kernel * too (^T in kern/tty.c). * * TODO - consider whether pctcpu should be used. */ #define ISRUN(p) (((p)->ki_stat == SRUN) || ((p)->ki_stat == SIDL)) #define TESTAB(a, b) ((a)<<1 | (b)) #define ONLYA 2 #define ONLYB 1 #define BOTH 3 int proc_compare(struct kinfo_proc *p1, struct kinfo_proc *p2) { if (p1 == NULL) return (1); /* * see if at least one of them is runnable */ switch (TESTAB(ISRUN(p1), ISRUN(p2))) { case ONLYA: return (0); case ONLYB: return (1); case BOTH: /* * tie - favor one with highest recent cpu utilization */ if (p2->ki_estcpu > p1->ki_estcpu) return (1); if (p1->ki_estcpu > p2->ki_estcpu) return (0); return (p2->ki_pid > p1->ki_pid); /* tie - return highest pid */ } /* * weed out zombies */ switch (TESTAB(p1->ki_stat == SZOMB, p2->ki_stat == SZOMB)) { case ONLYA: return (1); case ONLYB: return (0); case BOTH: return (p2->ki_pid > p1->ki_pid); /* tie - return highest pid */ } /* * pick the one with the smallest sleep time */ if (p2->ki_slptime > p1->ki_slptime) return (0); if (p1->ki_slptime > p2->ki_slptime) return (1); /* * favor one sleeping in a non-interruptible sleep */ if (p1->ki_tdflags & TDF_SINTR && (p2->ki_tdflags & TDF_SINTR) == 0) return (1); if (p2->ki_tdflags & TDF_SINTR && (p1->ki_tdflags & TDF_SINTR) == 0) return (0); return (p2->ki_pid > p1->ki_pid); /* tie - return highest pid */ } diff --git a/usr.bin/who/who.c b/usr.bin/who/who.c index c7331296913b..3416d5eac0c9 100644 --- a/usr.bin/who/who.c +++ b/usr.bin/who/who.c @@ -1,316 +1,315 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2002 Tim J. Robbins. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void heading(void); static void process_utmp(void); static void quick(void); static void row(const struct utmpx *); static int ttywidth(void); static void usage(void); static void whoami(void); static int Hflag; /* Write column headings */ static int aflag; /* Print all entries */ static int bflag; /* Show date of the last reboot */ static int mflag; /* Show info about current terminal */ static int qflag; /* "Quick" mode */ static int sflag; /* Show name, line, time */ static int Tflag; /* Show terminal state */ static int uflag; /* Show idle time */ int main(int argc, char *argv[]) { int ch; setlocale(LC_TIME, ""); while ((ch = getopt(argc, argv, "HTabmqsu")) != -1) { switch (ch) { case 'H': /* Write column headings */ Hflag = 1; break; case 'T': /* Show terminal state */ Tflag = 1; break; case 'a': /* Same as -bdlprtTu */ aflag = bflag = Tflag = uflag = 1; break; case 'b': /* Show date of the last reboot */ bflag = 1; break; case 'm': /* Show info about current terminal */ mflag = 1; break; case 'q': /* "Quick" mode */ qflag = 1; break; case 's': /* Show name, line, time */ sflag = 1; break; case 'u': /* Show idle time */ uflag = 1; break; default: usage(); /*NOTREACHED*/ } } argc -= optind; argv += optind; if (argc >= 2 && strcmp(argv[0], "am") == 0 && (strcmp(argv[1], "i") == 0 || strcmp(argv[1], "I") == 0)) { /* "who am i" or "who am I", equivalent to -m */ mflag = 1; argc -= 2; argv += 2; } if (argc > 1) usage(); if (*argv != NULL) { if (setutxdb(UTXDB_ACTIVE, *argv) != 0) err(1, "%s", *argv); } if (qflag) quick(); else { if (sflag) Tflag = uflag = 0; if (Hflag) heading(); if (mflag) whoami(); else process_utmp(); } endutxent(); exit(0); } static void usage(void) { fprintf(stderr, "usage: who [-abHmqsTu] [am I] [file]\n"); exit(1); } static void heading(void) { printf("%-16s ", "NAME"); if (Tflag) printf("S "); printf("%-12s %-12s ", "LINE", "TIME"); if (uflag) printf("IDLE "); printf("%-16s\n", "FROM"); } static void row(const struct utmpx *ut) { char buf[80], tty[PATH_MAX]; struct stat sb; time_t idle, t; static int d_first = -1; struct tm *tm; char state; if (d_first < 0) d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); state = '?'; idle = 0; if (Tflag || uflag) { snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, ut->ut_line); if (stat(tty, &sb) == 0) { state = sb.st_mode & (S_IWOTH|S_IWGRP) ? '+' : '-'; idle = time(NULL) - sb.st_mtime; } } printf("%-16s ", ut->ut_user); if (Tflag) printf("%c ", state); if (ut->ut_type == BOOT_TIME) printf("%-12s ", "system boot"); else printf("%-12s ", ut->ut_line); t = ut->ut_tv.tv_sec; tm = localtime(&t); strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm); printf("%-*s ", 12, buf); if (uflag) { if (idle < 60) printf(" . "); else if (idle < 24 * 60 * 60) printf("%02d:%02d ", (int)(idle / 60 / 60), (int)(idle / 60 % 60)); else printf(" old "); } if (*ut->ut_host != '\0') printf("(%s)", ut->ut_host); putchar('\n'); } static int ttystat(char *line) { struct stat sb; char ttybuf[MAXPATHLEN]; if (line == NULL) return (0); (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line); if (stat(ttybuf, &sb) == 0) { return (0); } else return (-1); } static void process_utmp(void) { struct utmpx *utx; while ((utx = getutxent()) != NULL) { if ((aflag || !bflag) && utx->ut_type == USER_PROCESS) { if (ttystat(utx->ut_line) == 0) row(utx); } else if (bflag && utx->ut_type == BOOT_TIME) row(utx); } } static void quick(void) { struct utmpx *utx; int col, ncols, num; ncols = ttywidth(); col = num = 0; while ((utx = getutxent()) != NULL) { if (utx->ut_type != USER_PROCESS) continue; printf("%-16s", utx->ut_user); if (++col < ncols / (16 + 1)) putchar(' '); else { col = 0; putchar('\n'); } num++; } if (col != 0) putchar('\n'); printf("# users = %d\n", num); } static void whoami(void) { struct utmpx ut, *utx; struct passwd *pwd; const char *name, *tty; if ((tty = ttyname(STDIN_FILENO)) == NULL) tty = "tty??"; else if (strncmp(tty, _PATH_DEV, sizeof _PATH_DEV - 1) == 0) tty += sizeof _PATH_DEV - 1; strlcpy(ut.ut_line, tty, sizeof ut.ut_line); /* Search utmp for our tty, dump first matching record. */ if ((utx = getutxline(&ut)) != NULL && utx->ut_type == USER_PROCESS) { row(utx); return; } /* Not found; fill the utmp structure with the information we have. */ memset(&ut, 0, sizeof(ut)); if ((pwd = getpwuid(getuid())) != NULL) name = pwd->pw_name; else name = "?"; strlcpy(ut.ut_user, name, sizeof ut.ut_user); gettimeofday(&ut.ut_tv, NULL); row(&ut); } static int ttywidth(void) { struct winsize ws; long width; char *cols, *ep; if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') { errno = 0; width = strtol(cols, &ep, 10); if (errno || width <= 0 || width > INT_MAX || ep == cols || *ep != '\0') warnx("invalid COLUMNS environment variable ignored"); else return (width); } if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) return (ws.ws_col); return (80); } diff --git a/usr.bin/ypwhich/ypwhich.c b/usr.bin/ypwhich/ypwhich.c index 20d9b8bc4687..a76fea2c4693 100644 --- a/usr.bin/ypwhich/ypwhich.c +++ b/usr.bin/ypwhich/ypwhich.c @@ -1,277 +1,276 @@ /* $OpenBSD: ypwhich.c,v 1.23 2015/02/08 23:40:35 deraadt Exp $ */ /* $NetBSD: ypwhich.c,v 1.6 1996/05/13 02:43:48 thorpej Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1992, 1993 Theo de Raadt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "yplib_host.h" static const struct ypalias { char *alias, *name; } ypaliases[] = { { "passwd", "passwd.byname" }, { "master.passwd", "master.passwd.byname" }, { "shadow", "shadow.byname" }, { "group", "group.byname" }, { "networks", "networks.byaddr" }, { "hosts", "hosts.byaddr" }, { "protocols", "protocols.bynumber" }, { "services", "services.byname" }, { "aliases", "mail.aliases" }, { "ethers", "ethers.byname" }, }; static void usage(void) { fprintf(stderr, "usage: ypwhich [-t] [-d domain] [[-h] host]\n" " ypwhich [-t] [-d domain] [-h host] -m [mname]\n" " ypwhich -x\n"); exit(1); } /* * Like yp_bind except can query a specific host */ static int bind_host(char *dom, struct sockaddr_in *sin) { struct hostent *hent = NULL; struct ypbind_resp ypbr; struct in_addr ss_addr; struct timeval tv; CLIENT *client; int sock, r; sock = RPC_ANYSOCK; tv.tv_sec = 15; tv.tv_usec = 0; client = clntudp_create(sin, YPBINDPROG, YPBINDVERS, tv, &sock); if (client == NULL) { warnx("host is not bound to a ypmaster"); return (YPERR_YPBIND); } tv.tv_sec = 5; tv.tv_usec = 0; r = clnt_call(client, YPBINDPROC_DOMAIN, (xdrproc_t)xdr_domainname, &dom, (xdrproc_t)xdr_ypbind_resp, &ypbr, tv); if (r != RPC_SUCCESS) { warnx("can't clnt_call: %s", yperr_string(YPERR_YPBIND)); clnt_destroy(client); return (YPERR_YPBIND); } else { if (ypbr.ypbind_status != YPBIND_SUCC_VAL) { warnx("can't yp_bind: reason: %s", yperr_string(ypbr.ypbind_status)); clnt_destroy(client); return (r); } } clnt_destroy(client); memmove(&ss_addr.s_addr, &ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr, sizeof (ss_addr)); hent = gethostbyaddr((char *)&ss_addr.s_addr, sizeof(ss_addr.s_addr), AF_INET); if (hent != NULL) printf("%s\n", hent->h_name); else printf("%s\n", inet_ntoa(ss_addr)); return (0); } int main(int argc, char *argv[]) { char *domain, *master, *map = NULL, *host = NULL; int notrans = 0, mode = 0, c, r, i; struct ypmaplist *ypml, *y; struct sockaddr_in sin; struct hostent *hent; CLIENT *client = NULL; yp_get_default_domain(&domain); if (domain == NULL) errx(1, "YP domain name not set"); while ((c = getopt(argc, argv, "xd:h:mt")) != -1) switch (c) { case 'x': for (i = 0; i < nitems(ypaliases); i++) printf("\"%s\" is an alias for \"%s\"\n", ypaliases[i].alias, ypaliases[i].name); exit(0); case 'h': host = optarg; break; case 'd': domain = optarg; break; case 't': notrans = 1; break; case 'm': mode = 1; break; default: usage(); } argc -= optind; argv += optind; if (mode == 0) { switch (argc) { case 0: memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (bind_host(domain, &sin)) exit(1); break; case 1: bzero(&sin, sizeof sin); sin.sin_family = AF_INET; if (inet_aton(argv[0], &sin.sin_addr) == 0) { hent = gethostbyname(argv[0]); if (!hent) { errx(1, "host %s unknown", argv[0]); } } if (bind_host(domain, &sin)) exit(1); break; default: usage(); } exit(0); } if (argc > 1) usage(); if (host != NULL) client = yp_bind_host(host, YPPROG, YPVERS, 0, 1); if (argv[0]) { map = argv[0]; if (notrans == 0) { for (i = 0; i < nitems(ypaliases); i++) if (strcmp(map, ypaliases[i].alias) == 0) map = ypaliases[i].name; } if (host != NULL) r = yp_master_host(client, domain, map, &master); else r = yp_master(domain, map, &master); switch (r) { case 0: printf("%s\n", master); free(master); break; case YPERR_YPBIND: errx(1, "not running ypbind"); default: errx(1, "can't find master for map %s: reason: %s", map, yperr_string(r)); } exit(0); } ypml = NULL; if (host != NULL) r = yp_maplist_host(client, domain, &ypml); else r = yp_maplist(domain, &ypml); r = 0; switch (r) { case 0: for (y = ypml; y; ) { ypml = y; if (host != NULL) { r = yp_master_host(client, domain, ypml->map, &master); } else { r = yp_master(domain, ypml->map, &master); } switch (r) { case 0: printf("%s %s\n", ypml->map, master); free(master); break; default: warnx("can't find the master of %s: reason: %s", ypml->map, yperr_string(r)); break; } y = ypml->next; free(ypml); } break; case YPERR_YPBIND: errx(1, "not running ypbind"); default: errx(1, "can't get map list for domain %s: reason: %s", domain, yperr_string(r)); } exit(0); }