From: Guus Sliepen Date: Fri, 12 Nov 2010 15:15:29 +0000 (+0100) Subject: Merge branch 'master' into 1.1 X-Git-Tag: release-1.1pre1~69 X-Git-Url: https://git.librecmc.org/?p=oweals%2Ftinc.git;a=commitdiff_plain;h=a22041922f160667573e9a5ae3f4195e1668906a Merge branch 'master' into 1.1 Conflicts: doc/tincd.8.in lib/pidfile.c src/graph.c src/net.c src/net.h src/net_packet.c src/net_setup.c src/net_socket.c src/netutl.c src/node.c src/node.h src/protocol_auth.c src/protocol_key.c src/tincd.c --- a22041922f160667573e9a5ae3f4195e1668906a diff --cc have.h index 89454fe,923e76a..21c16ef --- a/have.h +++ b/have.h @@@ -21,6 -21,14 +21,15 @@@ #ifndef __TINC_HAVE_H__ #define __TINC_HAVE_H__ + #ifdef HAVE_MINGW + #ifdef WITH_WINDOWS2000 + #define WINVER Windows2000 + #else + #define WINVER WindowsXP + #endif ++#define WIN32_LEAN_AND_MEAN + #endif + #include #include #include diff --cc src/conf.c index d5bc916,0727953..faff003 --- a/src/conf.c +++ b/src/conf.c @@@ -22,7 -23,8 +23,8 @@@ #include "system.h" -#include "avl_tree.h" +#include "splay_tree.h" + #include "connection.h" #include "conf.h" #include "logger.h" #include "netutl.h" /* for str2address */ @@@ -50,15 -59,15 +59,15 @@@ static int config_compare(const config_ if(result) return result; else - return strcmp(a->file, b->file); + return a->file ? strcmp(a->file, b->file) : 0; } -void init_configuration(avl_tree_t ** config_tree) { - *config_tree = avl_alloc_tree((avl_compare_t) config_compare, (avl_action_t) free_config); +void init_configuration(splay_tree_t ** config_tree) { + *config_tree = splay_alloc_tree((splay_compare_t) config_compare, (splay_action_t) free_config); } -void exit_configuration(avl_tree_t ** config_tree) { - avl_delete_tree(*config_tree); +void exit_configuration(splay_tree_t ** config_tree) { + splay_delete_tree(*config_tree); *config_tree = NULL; } @@@ -87,10 -96,10 +96,10 @@@ config_t *lookup_config(splay_tree_t *c config_t cfg, *found; cfg.variable = variable; - cfg.file = ""; + cfg.file = NULL; cfg.line = 0; - found = avl_search_closest_greater(config_tree, &cfg); + found = splay_search_closest_greater(config_tree, &cfg); if(!found) return NULL; @@@ -237,7 -285,7 +285,7 @@@ config_t *parse_config_line(char *line Parse a configuration file and put the results in the configuration tree starting at *base. */ - int read_config_file(splay_tree_t *config_tree, const char *fname) { -bool read_config_file(avl_tree_t *config_tree, const char *fname) { ++bool read_config_file(splay_tree_t *config_tree, const char *fname) { FILE *fp; char buffer[MAX_STRING_SIZE]; char *line; @@@ -316,6 -337,26 +337,26 @@@ return result; } -void read_config_options(avl_tree_t *config_tree, const char *prefix) { ++void read_config_options(splay_tree_t *config_tree, const char *prefix) { + list_node_t *node, *next; + size_t prefix_len = prefix ? strlen(prefix) : 0; + + for(node = cmdline_conf->tail; node; node = next) { + config_t *cfg = (config_t *)node->data; + next = node->prev; + + if(!prefix && strchr(cfg->variable, '.')) + continue; + + if(prefix && (strncmp(prefix, cfg->variable, prefix_len) || cfg->variable[prefix_len] != '.')) + continue; + + config_add(config_tree, cfg); + node->data = NULL; + list_unlink_node(cmdline_conf, node); + } + } + bool read_server_config() { char *fname; bool x; @@@ -332,6 -375,79 +375,19 @@@ return x; } + bool read_connection_config(connection_t *c) { + char *fname; + bool x; + + read_config_options(c->config_tree, c->name); + + xasprintf(&fname, "%s/hosts/%s", confbase, c->name); + x = read_config_file(c->config_tree, fname); + free(fname); + + return x; + } + -FILE *ask_and_open(const char *filename, const char *what) { - FILE *r; - char *directory; - char line[PATH_MAX]; - const char *fn; - - /* Check stdin and stdout */ - if(!isatty(0) || !isatty(1)) { - /* Argh, they are running us from a script or something. Write - the files to the current directory and let them burn in hell - for ever. */ - fn = filename; - } else { - /* Ask for a file and/or directory name. */ - fprintf(stdout, "Please enter a file to save %s to [%s]: ", - what, filename); - fflush(stdout); - - fn = readline(stdin, line, sizeof line); - - if(!fn) { - fprintf(stderr, "Error while reading stdin: %s\n", - strerror(errno)); - return NULL; - } - - if(!strlen(fn)) - /* User just pressed enter. */ - fn = filename; - } - -#ifdef HAVE_MINGW - if(fn[0] != '\\' && fn[0] != '/' && !strchr(fn, ':')) { -#else - if(fn[0] != '/') { -#endif - /* The directory is a relative path or a filename. */ - char *p; - - directory = get_current_dir_name(); - xasprintf(&p, "%s/%s", directory, fn); - free(directory); - fn = p; - } - - umask(0077); /* Disallow everything for group and other */ - - /* Open it first to keep the inode busy */ - - r = fopen(fn, "r+") ?: fopen(fn, "w+"); - - if(!r) { - fprintf(stderr, "Error opening file `%s': %s\n", - fn, strerror(errno)); - return NULL; - } - - return r; -} - bool disable_old_keys(FILE *f) { char buf[100]; long pos; diff --cc src/conf.h index be70c24,3eae4ad..cc57e82 --- a/src/conf.h +++ b/src/conf.h @@@ -21,7 -21,8 +21,8 @@@ #ifndef __TINC_CONF_H__ #define __TINC_CONF_H__ -#include "avl_tree.h" +#include "splay_tree.h" + #include "list.h" typedef struct config_t { char *variable; @@@ -40,23 -41,27 +41,27 @@@ extern int maxtimeout extern bool bypass_security; extern char *confbase; extern char *netname; + extern list_t *cmdline_conf; -extern void init_configuration(avl_tree_t **); -extern void exit_configuration(avl_tree_t **); +extern void init_configuration(splay_tree_t **); +extern void exit_configuration(splay_tree_t **); extern config_t *new_config(void) __attribute__ ((__malloc__)); extern void free_config(config_t *); -extern void config_add(avl_tree_t *, config_t *); -extern config_t *lookup_config(avl_tree_t *, char *); -extern config_t *lookup_config_next(avl_tree_t *, const config_t *); +extern void config_add(splay_tree_t *, config_t *); +extern config_t *lookup_config(splay_tree_t *, char *); +extern config_t *lookup_config_next(splay_tree_t *, const config_t *); extern bool get_config_bool(const config_t *, bool *); extern bool get_config_int(const config_t *, int *); extern bool get_config_string(const config_t *, char **); extern bool get_config_address(const config_t *, struct addrinfo **); extern bool get_config_subnet(const config_t *, struct subnet_t **); - extern int read_config_file(splay_tree_t *, const char *); + extern config_t *parse_config_line(char *, const char *, int); -extern bool read_config_file(avl_tree_t *, const char *); -extern void read_config_options(avl_tree_t *, const char *); ++extern bool read_config_file(splay_tree_t *, const char *); ++extern void read_config_options(splay_tree_t *, const char *); extern bool read_server_config(void); + extern bool read_connection_config(struct connection_t *); -extern FILE *ask_and_open(const char *, const char *); +extern FILE *ask_and_open(const char *, const char *, const char *); extern bool is_safe_path(const char *); extern bool disable_old_keys(FILE *); diff --cc src/connection.c index 18c03c7,ac946ab..fecb48d --- a/src/connection.c +++ b/src/connection.c @@@ -89,31 -109,21 +89,20 @@@ void connection_add(connection_t *c) } void connection_del(connection_t *c) { - avl_delete(connection_tree, c); + splay_delete(connection_tree, c); } -void dump_connections(void) { - avl_node_t *node; +bool dump_connections(connection_t *cdump) { + splay_node_t *node; connection_t *c; - logger(LOG_DEBUG, "Connections:"); - for(node = connection_tree->head; node; node = node->next) { c = node->data; - logger(LOG_DEBUG, " %s at %s options %x socket %d status %04x outbuf %d/%d/%d", - c->name, c->hostname, c->options, c->socket, bitfield_to_int(&c->status, sizeof c->status), - c->outbufsize, c->outbufstart, c->outbuflen); + send_request(cdump, "%d %d %s at %s options %x socket %d status %04x", + CONTROL, REQ_DUMP_CONNECTIONS, + c->name, c->hostname, c->options, c->socket, + bitfield_to_int(&c->status, sizeof c->status)); } - logger(LOG_DEBUG, "End of connections."); + return send_request(cdump, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS); } - - bool read_connection_config(connection_t *c) { - char *fname; - bool x; - - xasprintf(&fname, "%s/hosts/%s", confbase, c->name); - x = read_config_file(c->config_tree, fname); - free(fname); - - return x; - } diff --cc src/connection.h index 0f2b1d6,05e8b4b..0fc49ef --- a/src/connection.h +++ b/src/connection.h @@@ -99,7 -110,6 +99,6 @@@ extern connection_t *new_connection(voi extern void free_connection(connection_t *); extern void connection_add(connection_t *); extern void connection_del(connection_t *); -extern void dump_connections(void); +extern bool dump_connections(struct connection_t *); - extern bool read_connection_config(connection_t *); #endif /* __TINC_CONNECTION_H__ */ diff --cc src/getopt.c index b2f88b4,0000000..a6782ed mode 100644,000000..100644 --- a/src/getopt.c +++ b/src/getopt.c @@@ -1,1042 -1,0 +1,1048 @@@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97 + Free Software Foundation, Inc. + +NOTE: The canonical source of this file is maintained with the GNU C Library. +Bugs can be reported to bug-glibc@prep.ai.mit.edu. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + ++#ifdef HAVE_STRING_H ++#include ++#endif ++ +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#include +#endif /* GNU C library. */ + +#ifdef VMS +#include +#if HAVE_STRING_H - 0 +#include +#endif +#endif + +#if defined (WIN32) && !defined (__CYGWIN32__) +/* It's not Unix, really. See? Capital letters. */ +#include +#define getpid() GetCurrentProcessId() +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv (); + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#if !defined (__STDC__) || !__STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +extern pid_t __libc_pid; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((__unused__)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +text_set_element (__libc_subinit, store_args_and_env); + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined (__STDC__) && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len); + memset (&new_str[nonoption_flags_max_len], '\0', + top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined (__STDC__) && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + { + memcpy (__getopt_nonoption_flags, orig_str, len); + memset (&__getopt_nonoption_flags[len], '\0', + nonoption_flags_max_len - len); + } + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) - if (argv[optind - 1][1] == '-') - /* --option */ - fprintf (stderr, - "%s: option `--%s' doesn't allow an argument\n", - argv[0], pfound->name); - else - /* +option or -option */ - fprintf (stderr, - "%s: option `%c%s' doesn't allow an argument\n", - argv[0], argv[optind - 1][0], pfound->name); ++ { ++ if (argv[optind - 1][1] == '-') ++ /* --option */ ++ fprintf (stderr, ++ "%s: option `--%s' doesn't allow an argument\n", ++ argv[0], pfound->name); ++ else ++ /* +option or -option */ ++ fprintf (stderr, ++ "%s: option `%c%s' doesn't allow an argument\n", ++ argv[0], argv[optind - 1][0], pfound->name); ++ } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", + argv[0], c); + else + fprintf (stderr, "%s: invalid option -- %c\n", + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `-W %s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, + "%s: option `-W %s' doesn't allow an argument\n", + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + "%s: option requires an argument -- %c\n", + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --cc src/logger.c index 35a6a38,bc20438..4c0d231 --- a/src/logger.c +++ b/src/logger.c @@@ -85,8 -85,8 +85,8 @@@ void logger(int priority, const char *f #ifdef HAVE_MINGW { char message[4096]; - char *messages[] = {message}; + const char *messages[] = {message}; - vsnprintf(message, sizeof(message), format, ap); + vsnprintf(message, sizeof message, format, ap); ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL); } #else diff --cc src/memcmp.c index 9d80833,0000000..8103e1a mode 100644,000000..100644 --- a/src/memcmp.c +++ b/src/memcmp.c @@@ -1,391 -1,0 +1,391 @@@ +/* Copyright (C) 1991, 1993, 1995, 1997, 1998 Free Software Foundation, Inc. + Contributed by Torbjorn Granlund (tege@sics.se). + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@prep.ai.mit.edu. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#undef __ptr_t +#if defined __cplusplus || (defined __STDC__ && __STDC__) +# define __ptr_t void * +#else /* Not C++ or ANSI C. */ +# undef const +# define const +# define __ptr_t char * +#endif /* C++ or ANSI C. */ + +#ifndef __P +# if defined __GNUC__ || (defined __STDC__ && __STDC__) +# define __P(args) args +# else +# define __P(args) () +# endif /* GCC. */ +#endif /* Not __P. */ + +#if defined HAVE_STRING_H || defined _LIBC +# include +#endif + +#undef memcmp + +#ifdef _LIBC + +# include + +#else /* Not in the GNU C library. */ + +# include + +/* Type to use for aligned memory operations. + This should normally be the biggest type supported by a single load + and store. Must be an unsigned type. */ +# define op_t unsigned long int +# define OPSIZ (sizeof(op_t)) + +/* Threshold value for when to enter the unrolled loops. */ +# define OP_T_THRES 16 + +/* Type to use for unaligned operations. */ +typedef unsigned char byte; + +# ifndef WORDS_BIGENDIAN +# define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2))) +# else +# define MERGE(w0, sh_1, w1, sh_2) (((w0) << (sh_1)) | ((w1) >> (sh_2))) +# endif + +#endif /* In the GNU C library. */ + +#ifdef WORDS_BIGENDIAN +# define CMP_LT_OR_GT(a, b) ((a) > (b) ? 1 : -1) +#else +# define CMP_LT_OR_GT(a, b) memcmp_bytes ((a), (b)) +#endif + +/* BE VERY CAREFUL IF YOU CHANGE THIS CODE! */ + +/* The strategy of this memcmp is: + + 1. Compare bytes until one of the block pointers is aligned. + + 2. Compare using memcmp_common_alignment or + memcmp_not_common_alignment, regarding the alignment of the other + block after the initial byte operations. The maximum number of + full words (of type op_t) are compared in this way. + + 3. Compare the few remaining bytes. */ + +#ifndef WORDS_BIGENDIAN +/* memcmp_bytes -- Compare A and B bytewise in the byte order of the machine. + A and B are known to be different. + This is needed only on little-endian machines. */ + +static int memcmp_bytes __P((op_t, op_t)); + +# ifdef __GNUC__ +__inline +# endif +static int +memcmp_bytes (a, b) + op_t a, b; +{ - long int srcp1 = (long int) &a; - long int srcp2 = (long int) &b; ++ intptr_t srcp1 = (intptr_t) &a; ++ intptr_t srcp2 = (intptr_t) &b; + op_t a0, b0; + + do + { + a0 = ((byte *) srcp1)[0]; + b0 = ((byte *) srcp2)[0]; + srcp1 += 1; + srcp2 += 1; + } + while (a0 == b0); + return a0 - b0; +} +#endif + - static int memcmp_common_alignment __P((long, long, size_t)); ++static int memcmp_common_alignment __P((intptr_t, intptr_t, size_t)); + +/* memcmp_common_alignment -- Compare blocks at SRCP1 and SRCP2 with LEN `op_t' + objects (not LEN bytes!). Both SRCP1 and SRCP2 should be aligned for + memory operations on `op_t's. */ +#ifdef __GNUC__ +__inline +#endif +static int +memcmp_common_alignment (srcp1, srcp2, len) - long int srcp1; - long int srcp2; ++ intptr_t srcp1; ++ intptr_t srcp2; + size_t len; +{ + op_t a0, a1; + op_t b0, b1; + + switch (len % 4) + { + default: /* Avoid warning about uninitialized local variables. */ + case 2: + a0 = ((op_t *) srcp1)[0]; + b0 = ((op_t *) srcp2)[0]; + srcp1 -= 2 * OPSIZ; + srcp2 -= 2 * OPSIZ; + len += 2; + goto do1; + case 3: + a1 = ((op_t *) srcp1)[0]; + b1 = ((op_t *) srcp2)[0]; + srcp1 -= OPSIZ; + srcp2 -= OPSIZ; + len += 1; + goto do2; + case 0: + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + return 0; + a0 = ((op_t *) srcp1)[0]; + b0 = ((op_t *) srcp2)[0]; + goto do3; + case 1: + a1 = ((op_t *) srcp1)[0]; + b1 = ((op_t *) srcp2)[0]; + srcp1 += OPSIZ; + srcp2 += OPSIZ; + len -= 1; + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + goto do0; + /* Fall through. */ + } + + do + { + a0 = ((op_t *) srcp1)[0]; + b0 = ((op_t *) srcp2)[0]; + if (a1 != b1) + return CMP_LT_OR_GT (a1, b1); + + do3: + a1 = ((op_t *) srcp1)[1]; + b1 = ((op_t *) srcp2)[1]; + if (a0 != b0) + return CMP_LT_OR_GT (a0, b0); + + do2: + a0 = ((op_t *) srcp1)[2]; + b0 = ((op_t *) srcp2)[2]; + if (a1 != b1) + return CMP_LT_OR_GT (a1, b1); + + do1: + a1 = ((op_t *) srcp1)[3]; + b1 = ((op_t *) srcp2)[3]; + if (a0 != b0) + return CMP_LT_OR_GT (a0, b0); + + srcp1 += 4 * OPSIZ; + srcp2 += 4 * OPSIZ; + len -= 4; + } + while (len != 0); + + /* This is the right position for do0. Please don't move + it into the loop. */ + do0: + if (a1 != b1) + return CMP_LT_OR_GT (a1, b1); + return 0; +} + - static int memcmp_not_common_alignment __P((long, long, size_t)); ++static int memcmp_not_common_alignment __P((intptr_t, intptr_t, size_t)); + +/* memcmp_not_common_alignment -- Compare blocks at SRCP1 and SRCP2 with LEN + `op_t' objects (not LEN bytes!). SRCP2 should be aligned for memory + operations on `op_t', but SRCP1 *should be unaligned*. */ +#ifdef __GNUC__ +__inline +#endif +static int +memcmp_not_common_alignment (srcp1, srcp2, len) - long int srcp1; - long int srcp2; ++ intptr_t srcp1; ++ intptr_t srcp2; + size_t len; +{ + op_t a0, a1, a2, a3; + op_t b0, b1, b2, b3; + op_t x; + int shl, shr; + + /* Calculate how to shift a word read at the memory operation + aligned srcp1 to make it aligned for comparison. */ + + shl = 8 * (srcp1 % OPSIZ); + shr = 8 * OPSIZ - shl; + + /* Make SRCP1 aligned by rounding it down to the beginning of the `op_t' + it points in the middle of. */ + srcp1 &= -OPSIZ; + + switch (len % 4) + { + default: /* Avoid warning about uninitialized local variables. */ + case 2: + a1 = ((op_t *) srcp1)[0]; + a2 = ((op_t *) srcp1)[1]; + b2 = ((op_t *) srcp2)[0]; + srcp1 -= 1 * OPSIZ; + srcp2 -= 2 * OPSIZ; + len += 2; + goto do1; + case 3: + a0 = ((op_t *) srcp1)[0]; + a1 = ((op_t *) srcp1)[1]; + b1 = ((op_t *) srcp2)[0]; + srcp2 -= 1 * OPSIZ; + len += 1; + goto do2; + case 0: + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + return 0; + a3 = ((op_t *) srcp1)[0]; + a0 = ((op_t *) srcp1)[1]; + b0 = ((op_t *) srcp2)[0]; + srcp1 += 1 * OPSIZ; + goto do3; + case 1: + a2 = ((op_t *) srcp1)[0]; + a3 = ((op_t *) srcp1)[1]; + b3 = ((op_t *) srcp2)[0]; + srcp1 += 2 * OPSIZ; + srcp2 += 1 * OPSIZ; + len -= 1; + if (OP_T_THRES <= 3 * OPSIZ && len == 0) + goto do0; + /* Fall through. */ + } + + do + { + a0 = ((op_t *) srcp1)[0]; + b0 = ((op_t *) srcp2)[0]; + x = MERGE(a2, shl, a3, shr); + if (x != b3) + return CMP_LT_OR_GT (x, b3); + + do3: + a1 = ((op_t *) srcp1)[1]; + b1 = ((op_t *) srcp2)[1]; + x = MERGE(a3, shl, a0, shr); + if (x != b0) + return CMP_LT_OR_GT (x, b0); + + do2: + a2 = ((op_t *) srcp1)[2]; + b2 = ((op_t *) srcp2)[2]; + x = MERGE(a0, shl, a1, shr); + if (x != b1) + return CMP_LT_OR_GT (x, b1); + + do1: + a3 = ((op_t *) srcp1)[3]; + b3 = ((op_t *) srcp2)[3]; + x = MERGE(a1, shl, a2, shr); + if (x != b2) + return CMP_LT_OR_GT (x, b2); + + srcp1 += 4 * OPSIZ; + srcp2 += 4 * OPSIZ; + len -= 4; + } + while (len != 0); + + /* This is the right position for do0. Please don't move + it into the loop. */ + do0: + x = MERGE(a2, shl, a3, shr); + if (x != b3) + return CMP_LT_OR_GT (x, b3); + return 0; +} + +int +rpl_memcmp (s1, s2, len) + const __ptr_t s1; + const __ptr_t s2; + size_t len; +{ + op_t a0; + op_t b0; - long int srcp1 = (long int) s1; - long int srcp2 = (long int) s2; ++ intptr_t srcp1 = (intptr_t) s1; ++ intptr_t srcp2 = (intptr_t) s2; + op_t res; + + if (len >= OP_T_THRES) + { + /* There are at least some bytes to compare. No need to test + for LEN == 0 in this alignment loop. */ + while (srcp2 % OPSIZ != 0) + { + a0 = ((byte *) srcp1)[0]; + b0 = ((byte *) srcp2)[0]; + srcp1 += 1; + srcp2 += 1; + res = a0 - b0; + if (res != 0) + return res; + len -= 1; + } + + /* SRCP2 is now aligned for memory operations on `op_t'. + SRCP1 alignment determines if we can do a simple, + aligned compare or need to shuffle bits. */ + + if (srcp1 % OPSIZ == 0) + res = memcmp_common_alignment (srcp1, srcp2, len / OPSIZ); + else + res = memcmp_not_common_alignment (srcp1, srcp2, len / OPSIZ); + if (res != 0) + return res; + + /* Number of bytes remaining in the interval [0..OPSIZ-1]. */ + srcp1 += len & -OPSIZ; + srcp2 += len & -OPSIZ; + len %= OPSIZ; + } + + /* There are just a few bytes to compare. Use byte memory operations. */ + while (len != 0) + { + a0 = ((byte *) srcp1)[0]; + b0 = ((byte *) srcp2)[0]; + srcp1 += 1; + srcp2 += 1; + res = a0 - b0; + if (res != 0) + return res; + len -= 1; + } + + return 0; +} + +#ifdef weak_alias +# undef bcmp +weak_alias (memcmp, bcmp) +#endif diff --cc src/net.c index 0e58236,08e3cad..7d44d17 --- a/src/net.c +++ b/src/net.c @@@ -36,10 -39,18 +36,13 @@@ #include "subnet.h" #include "xalloc.h" -bool do_purge = false; -volatile bool running = false; - -time_t now = 0; + int contradicting_add_edge = 0; + int contradicting_del_edge = 0; + /* Purge edges and subnets of unreachable nodes. Use carefully. */ -static void purge(void) { - avl_node_t *nnode, *nnext, *enode, *enext, *snode, *snext; +void purge(void) { + splay_node_t *nnode, *nnext, *enode, *enext, *snode, *snext; node_t *n; edge_t *e; subnet_t *s; @@@ -187,30 -257,82 +190,43 @@@ static void timeout_handler(int fd, sho } } } - - if(c->outbuflen > 0 && c->last_flushed_time + pingtimeout < now) { - if(c->status.active) { - ifdebug(CONNECTIONS) logger(LOG_INFO, - "%s (%s) could not flush for %ld seconds (%d bytes remaining)", - c->name, c->hostname, now - c->last_flushed_time, c->outbuflen); - c->status.timeout = true; - terminate_connection(c, true); - } - } - } -} - -/* - check all connections to see if anything - happened on their sockets -*/ -static void check_network_activity(fd_set * readset, fd_set * writeset) { - connection_t *c; - avl_node_t *node; - int result, i; - socklen_t len = sizeof(result); - vpn_packet_t packet; - static int errors = 0; - - /* check input from kernel */ - if(device_fd >= 0 && FD_ISSET(device_fd, readset)) { - if(read_packet(&packet)) { - errors = 0; - packet.priority = 0; - route(myself, &packet); - } else { - usleep(errors * 50000); - errors++; - if(errors > 10) { - logger(LOG_ERR, "Too many errors from %s, exiting!", device); - running = false; - } - } } - /* check meta connections */ - for(node = connection_tree->head; node; node = node->next) { - c = node->data; ++ if(contradicting_del_edge && contradicting_add_edge) { ++ logger(LOG_WARNING, "Possible node with same Name as us!"); + - if(c->status.remove) - continue; - - if(FD_ISSET(c->socket, readset)) { - if(c->status.connecting) { - c->status.connecting = false; - getsockopt(c->socket, SOL_SOCKET, SO_ERROR, (void *)&result, &len); ++ if(rand() % 3 == 0) { ++ logger(LOG_ERR, "Shutting down, check configuration of all nodes for duplicate Names!"); ++ event_loopexit(NULL); ++ return; ++ } + - if(!result) - finish_connecting(c); - else { - ifdebug(CONNECTIONS) logger(LOG_DEBUG, - "Error while connecting to %s (%s): %s", - c->name, c->hostname, sockstrerror(result)); - closesocket(c->socket); - do_outgoing_connection(c); - continue; - } - } ++ contradicting_add_edge = 0; ++ contradicting_del_edge = 0; ++ } + - if(!receive_meta(c)) { - terminate_connection(c, c->status.active); - continue; - } - } + event_add(event, &(struct timeval){pingtimeout, 0}); +} - if(FD_ISSET(c->socket, writeset)) { - if(!flush_meta(c)) { - terminate_connection(c, c->status.active); - continue; - } +void handle_meta_connection_data(int fd, short events, void *data) { + connection_t *c = data; + int result; + socklen_t len = sizeof result; + + if(c->status.connecting) { + c->status.connecting = false; + + getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len); + + if(!result) + finish_connecting(c); + else { + ifdebug(CONNECTIONS) logger(LOG_DEBUG, + "Error while connecting to %s (%s): %s", + c->name, c->hostname, sockstrerror(result)); + closesocket(c->socket); + do_outgoing_connection(c); + return; } } @@@ -220,145 -345,216 +236,146 @@@ } } -/* - this is where it all happens... -*/ -int main_loop(void) { - fd_set readset, writeset; - struct timeval tv; - int r, maxfd; - time_t last_ping_check, last_config_check, last_graph_dump; - event_t *event; - - last_ping_check = now; - last_config_check = now; - last_graph_dump = now; - - srand(now); - - running = true; +static void sigterm_handler(int signal, short events, void *data) { + logger(LOG_NOTICE, "Got %s signal", strsignal(signal)); + event_loopexit(NULL); +} - while(running) { - now = time(NULL); +static void sighup_handler(int signal, short events, void *data) { + logger(LOG_NOTICE, "Got %s signal", strsignal(signal)); + reload_configuration(); +} - // tv.tv_sec = 1 + (rand() & 7); /* Approx. 5 seconds, randomized to prevent global synchronisation effects */ - tv.tv_sec = 1; - tv.tv_usec = 0; +int reload_configuration(void) { + connection_t *c; + splay_node_t *node, *next; + char *fname; + struct stat s; + static time_t last_config_check = 0; - maxfd = build_fdset(&readset, &writeset); + /* Reread our own configuration file */ -#ifdef HAVE_MINGW - LeaveCriticalSection(&mutex); -#endif - r = select(maxfd + 1, &readset, &writeset, NULL, &tv); -#ifdef HAVE_MINGW - EnterCriticalSection(&mutex); -#endif + exit_configuration(&config_tree); + init_configuration(&config_tree); - if(r < 0) { - if(!sockwouldblock(sockerrno)) { - logger(LOG_ERR, "Error while waiting for input: %s", sockstrerror(sockerrno)); - dump_connections(); - return 1; - } - } - - if(r > 0) - check_network_activity(&readset, &writeset); + if(!read_server_config()) { + logger(LOG_ERR, "Unable to reread configuration file, exitting."); + event_loopexit(NULL); + return EINVAL; + } - if(do_purge) { - purge(); - do_purge = false; + /* Close connections to hosts that have a changed or deleted host config file */ + + for(node = connection_tree->head; node; node = next) { + c = node->data; + next = node->next; + + if(c->outgoing) { + free(c->outgoing->name); + if(c->outgoing->ai) + freeaddrinfo(c->outgoing->ai); + free(c->outgoing); + c->outgoing = NULL; } + + xasprintf(&fname, "%s/hosts/%s", confbase, c->name); + if(stat(fname, &s) || s.st_mtime > last_config_check) + terminate_connection(c, c->status.active); + free(fname); + } - /* Let's check if everybody is still alive */ - - if(last_ping_check + pingtimeout < now) { - check_dead_connections(); - last_ping_check = now; - - if(routing_mode == RMODE_SWITCH) - age_subnets(); - - age_past_requests(); - - /* Should we regenerate our key? */ - - if(keyexpires < now) { - avl_node_t *node; - node_t *n; + last_config_check = time(NULL); - ifdebug(STATUS) logger(LOG_INFO, "Expiring symmetric keys"); + /* If StrictSubnet is set, expire deleted Subnets and read new ones in */ - for(node = node_tree->head; node; node = node->next) { - n = node->data; - if(n->inkey) { - free(n->inkey); - n->inkey = NULL; - } - } + if(strictsubnets) { + subnet_t *subnet; - send_key_changed(broadcast, myself); - keyexpires = now + keylifetime; - } + - if(contradicting_del_edge && contradicting_add_edge) { - logger(LOG_WARNING, "Possible node with same Name as us!"); - - if(rand() % 3 == 0) { - logger(LOG_ERR, "Shutting down, check configuration of all nodes for duplicate Names!"); - running = false; - break; - } - - contradicting_add_edge = 0; - contradicting_del_edge = 0; - } + for(node = subnet_tree->head; node; node = node->next) { + subnet = node->data; + subnet->expires = 1; } - if(sigalrm) { - avl_node_t *node; - logger(LOG_INFO, "Flushing event queue"); - expire_events(); - for(node = connection_tree->head; node; node = node->next) { - connection_t *c = node->data; - send_ping(c); + load_all_subnets(); + + for(node = subnet_tree->head; node; node = next) { + next = node->next; + subnet = node->data; + if(subnet->expires == 1) { + send_del_subnet(broadcast, subnet); + if(subnet->owner->status.reachable) + subnet_update(subnet->owner, subnet, false); + subnet_del(subnet->owner, subnet); + } else if(subnet->expires == -1) { + subnet->expires = 0; + } else { + send_add_subnet(broadcast, subnet); + if(subnet->owner->status.reachable) + subnet_update(subnet->owner, subnet, true); } - sigalrm = false; - } - - while((event = get_expired_event())) { - event->handler(event->data); - free_event(event); } + } - if(sighup) { - connection_t *c; - avl_node_t *node, *next; - char *fname; - struct stat s; - - sighup = false; - - /* Reread our own configuration file */ - - exit_configuration(&config_tree); - init_configuration(&config_tree); - - if(!read_server_config()) { - logger(LOG_ERR, "Unable to reread configuration file, exitting."); - return 1; - } - - /* Cancel non-active outgoing connections */ - - for(node = connection_tree->head; node; node = next) { - next = node->next; - c = node->data; - - c->outgoing = NULL; - - if(c->status.connecting) { - terminate_connection(c, false); - connection_del(c); - } - } - - /* Wipe list of outgoing connections */ - - for(list_node_t *node = outgoing_list->head; node; node = node->next) { - outgoing_t *outgoing = node->data; - - if(outgoing->event) - event_del(outgoing->event); - } - - list_delete_list(outgoing_list); - - /* Close connections to hosts that have a changed or deleted host config file */ - - for(node = connection_tree->head; node; node = node->next) { - c = node->data; - - xasprintf(&fname, "%s/hosts/%s", confbase, c->name); - if(stat(fname, &s) || s.st_mtime > last_config_check) - terminate_connection(c, c->status.active); - free(fname); - } + /* Try to make outgoing connections */ + + try_outgoing_connections(); - last_config_check = now; + return 0; +} - /* If StrictSubnet is set, expire deleted Subnets and read new ones in */ +void retry(void) { + connection_t *c; + splay_node_t *node; - if(strictsubnets) { - subnet_t *subnet; + for(node = connection_tree->head; node; node = node->next) { + c = node->data; + + if(c->outgoing && !c->node) { + if(timeout_initialized(&c->outgoing->ev)) + event_del(&c->outgoing->ev); + if(c->status.connecting) + close(c->socket); + c->outgoing->timeout = 0; + do_outgoing_connection(c); + } + } +} - for(node = subnet_tree->head; node; node = node->next) { - subnet = node->data; - subnet->expires = 1; - } +/* + this is where it all happens... +*/ +int main_loop(void) { + struct event timeout_event; + struct event sighup_event; + struct event sigterm_event; + struct event sigquit_event; - load_all_subnets(); - - for(node = subnet_tree->head; node; node = next) { - next = node->next; - subnet = node->data; - if(subnet->expires == 1) { - send_del_subnet(broadcast, subnet); - if(subnet->owner->status.reachable) - subnet_update(subnet->owner, subnet, false); - subnet_del(subnet->owner, subnet); - } else if(subnet->expires == -1) { - subnet->expires = 0; - } else { - send_add_subnet(broadcast, subnet); - if(subnet->owner->status.reachable) - subnet_update(subnet->owner, subnet, true); - } - } - } + timeout_set(&timeout_event, timeout_handler, &timeout_event); + event_add(&timeout_event, &(struct timeval){pingtimeout, 0}); - /* Try to make outgoing connections */ - - try_outgoing_connections(); - } - - /* Dump graph if wanted every 60 seconds*/ +#ifdef SIGHUP + signal_set(&sighup_event, SIGHUP, sighup_handler, NULL); + signal_add(&sighup_event, NULL); +#endif +#ifdef SIGTERM + signal_set(&sigterm_event, SIGTERM, sigterm_handler, NULL); + signal_add(&sigterm_event, NULL); +#endif +#ifdef SIGQUIT + signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL); + signal_add(&sigquit_event, NULL); +#endif - if(last_graph_dump + 60 < now) { - dump_graph(); - last_graph_dump = now; - } + if(event_loop(0) < 0) { + logger(LOG_ERR, "Error while waiting for input: %s", strerror(errno)); + return 1; } + signal_del(&sighup_event); + signal_del(&sigterm_event); + signal_del(&sigquit_event); + event_del(&timeout_event); + return 0; } diff --cc src/net.h index cd97e30,eae979c..f53c27a --- a/src/net.h +++ b/src/net.h @@@ -111,9 -109,14 +111,11 @@@ extern int addressfamily extern listen_socket_t listen_socket[MAXSOCKETS]; extern int listen_sockets; -extern int keyexpires; extern int keylifetime; extern bool do_prune; -extern bool do_purge; extern char *myport; -extern time_t now; + extern int contradicting_add_edge; + extern int contradicting_del_edge; /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */ #include "connection.h" diff --cc src/net_packet.c index 46d4e1a,44ab55d..b444bc9 --- a/src/net_packet.c +++ b/src/net_packet.c @@@ -355,8 -365,10 +356,10 @@@ static void send_udppacket(node_t *n, v int nextpkt = 0; vpn_packet_t *outpkt; int origlen; - int outlen, outpad; + size_t outlen; + #if defined(SOL_IP) && defined(IP_TOS) static int priority = 0; + #endif int origpriority; int sock; diff --cc src/net_setup.c index 198da3d,f4e5637..9c18895 --- a/src/net_setup.c +++ b/src/net_setup.c @@@ -71,32 -107,59 +71,32 @@@ bool read_rsa_public_key(connection_t * return false; } - /* Else, check if a harnessed public key is in the config file */ - - xasprintf(&fname, "%s/hosts/%s", confbase, c->name); - fp = fopen(fname, "r"); - - if(fp) { - c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL); - fclose(fp); - } - - free(fname); - - if(c->rsa_key) - return true; - - /* Try again with PEM_read_RSA_PUBKEY. */ - - xasprintf(&fname, "%s/hosts/%s", confbase, c->name); - fp = fopen(fname, "r"); - - if(fp) { - c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL); -// RSA_blinding_on(c->rsa_key, NULL); - fclose(fp); - } + result = rsa_read_pem_public_key(&c->rsa, fp); + fclose(fp); + if(!result) + logger(LOG_ERR, "Reading RSA public key file `%s' failed: %s", fname, strerror(errno)); free(fname); - - if(c->rsa_key) - return true; - - logger(LOG_ERR, "No public key for %s specified!", c->name); - - return false; + return result; } -bool read_rsa_private_key(void) { +bool read_rsa_private_key() { FILE *fp; - char *fname, *key, *pubkey; - struct stat s; + char *fname; + char *n, *d; + bool result; - if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key)) { - if(!get_config_string(lookup_config(config_tree, "PublicKey"), &pubkey)) { + /* First, check for simple PrivateKey statement */ + + if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) { - if(!get_config_string(lookup_config(myself->connection->config_tree, "PublicKey"), &n)) { ++ if(!get_config_string(lookup_config(config_tree, "PublicKey"), &n)) { logger(LOG_ERR, "PrivateKey used but no PublicKey found!"); + free(d); return false; } - myself->connection->rsa_key = RSA_new(); -// RSA_blinding_on(myself->connection->rsa_key, NULL); - BN_hex2bn(&myself->connection->rsa_key->d, key); - BN_hex2bn(&myself->connection->rsa_key->n, pubkey); - BN_hex2bn(&myself->connection->rsa_key->e, "FFFF"); - free(key); - free(pubkey); + result = rsa_set_hex_private_key(&myself->connection->rsa, n, "FFFF", d); + free(n); + free(d); return true; } @@@ -386,36 -423,65 +376,36 @@@ bool setup_myself(void) /* Generate packet encryption key */ - if(!get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher)) - if(get_config_string - (lookup_config(config_tree, "Cipher"), &cipher)) { - if(!strcasecmp(cipher, "none")) { - myself->incipher = NULL; - } else { - myself->incipher = EVP_get_cipherbyname(cipher); ++ if(!get_config_string(lookup_config(config_tree, "Cipher"), &cipher)) + cipher = xstrdup("blowfish"); - if(!myself->incipher) { - logger(LOG_ERR, "Unrecognized cipher type!"); - return false; - } - } - } else - myself->incipher = EVP_bf_cbc(); - - if(myself->incipher) - myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len; - else - myself->inkeylength = 1; - - myself->connection->outcipher = EVP_bf_ofb(); + if(!cipher_open_by_name(&myself->incipher, cipher)) { + logger(LOG_ERR, "Unrecognized cipher type!"); + return false; + } if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime)) keylifetime = 3600; - keyexpires = now + keylifetime; - + regenerate_key(); + /* Check if we want to use message authentication codes... */ - if(get_config_string(lookup_config(config_tree, "Digest"), &digest)) { - if(!strcasecmp(digest, "none")) { - myself->indigest = NULL; - } else { - myself->indigest = EVP_get_digestbyname(digest); + if(!get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest)) + digest = xstrdup("sha1"); - if(!myself->indigest) { - logger(LOG_ERR, "Unrecognized digest type!"); - return false; - } - } - } else - myself->indigest = EVP_sha1(); - - myself->connection->outdigest = EVP_sha1(); - - if(get_config_int(lookup_config(config_tree, "MACLength"), &myself->inmaclength)) { - if(myself->indigest) { - if(myself->inmaclength > myself->indigest->md_size) { - logger(LOG_ERR, "MAC length exceeds size of digest!"); - return false; - } else if(myself->inmaclength < 0) { - logger(LOG_ERR, "Bogus MAC length!"); - return false; - } - } - } else - myself->inmaclength = 4; + int maclength = 4; - get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &maclength); ++ get_config_int(lookup_config(config_tree, "MACLength"), &maclength); + + if(maclength < 0) { + logger(LOG_ERR, "Bogus MAC length!"); + return false; + } - myself->connection->outmaclength = 0; + if(!digest_open_by_name(&myself->indigest, digest, maclength)) { + logger(LOG_ERR, "Unrecognized digest type!"); + return false; + } /* Compression */ diff --cc src/net_socket.c index 4fe6516,762c0a2..44d7f77 --- a/src/net_socket.c +++ b/src/net_socket.c @@@ -69,12 -70,12 +69,12 @@@ static void configure_tcp(connection_t #if defined(SOL_TCP) && defined(TCP_NODELAY) option = 1; - setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof option); - setsockopt(c->socket, SOL_TCP, TCP_NODELAY, (void *)&option, sizeof(option)); ++ setsockopt(c->socket, SOL_TCP, TCP_NODELAY, (void *)&option, sizeof option); #endif #if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY) option = IPTOS_LOWDELAY; - setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof option); - setsockopt(c->socket, SOL_IP, IP_TOS, (void *)&option, sizeof(option)); ++ setsockopt(c->socket, SOL_IP, IP_TOS, (void *)&option, sizeof option); #endif } @@@ -180,7 -181,7 +180,7 @@@ int setup_listen_socket(const sockaddr_ /* Optimize TCP settings */ option = 1; - setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option); - setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option)); ++ setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof option); #if defined(SOL_IPV6) && defined(IPV6_V6ONLY) if(sa->sa.sa_family == AF_INET6) @@@ -192,10 -193,10 +192,10 @@@ #if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE) struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); + memset(&ifr, 0, sizeof ifr); strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ); - if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof ifr)) { - if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr))) { ++ if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof ifr)) { closesocket(nfd); logger(LOG_ERR, "Can't bind to interface %s: %s", iface, strerror(sockerrno)); @@@ -258,7 -259,7 +258,7 @@@ int setup_vpn_in_socket(const sockaddr_ #endif option = 1; - setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option); - setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option)); ++ setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof option); #if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) if(sa->sa.sa_family == AF_INET6) diff --cc src/process.c index 09fd63e,f2fff1d..77454f7 --- a/src/process.c +++ b/src/process.c @@@ -37,11 -37,18 +37,13 @@@ bool do_detach = true bool sigalrm = false; extern char *identname; -extern char *pidfilename; extern char **g_argv; extern bool use_logfile; -extern volatile bool running; + #ifndef HAVE_MINGW sigset_t emptysigset; + #endif -static int saved_debug_level = -1; - static void memory_full(int size) { logger(LOG_ERR, "Memory exhausted (couldn't allocate %d bytes), exitting.", size); exit(1); diff --cc src/tincd.c index 2162364,3dab9a0..c4750da --- a/src/tincd.c +++ b/src/tincd.c @@@ -119,26 -129,33 +122,31 @@@ static void usage(bool status) program_name); else { printf("Usage: %s [option]...\n\n", program_name); - printf(" -c, --config=DIR Read configuration options from DIR.\n" - " -D, --no-detach Don't fork and detach.\n" - " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n" - " -k, --kill[=SIGNAL] Attempt to kill a running tincd and exit.\n" - " -n, --net=NETNAME Connect to net NETNAME.\n" - " -K, --generate-keys[=BITS] Generate public/private RSA keypair.\n" - " -L, --mlock Lock tinc into main memory.\n" - " --logfile[=FILENAME] Write log entries to a logfile.\n" - " --pidfile=FILENAME Write PID to FILENAME.\n" - " -o [HOST.]KEY=VALUE Set global/host configuration value.\n" - " -R, --chroot chroot to NET dir at startup.\n" - " -U, --user=USER setuid to given USER at startup.\n" - " --help Display this help and exit.\n" - " --version Output version information and exit.\n\n"); + printf( " -c, --config=DIR Read configuration options from DIR.\n" + " -D, --no-detach Don't fork and detach.\n" + " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n" + " -n, --net=NETNAME Connect to net NETNAME.\n" + " -L, --mlock Lock tinc into main memory.\n" + " --logfile[=FILENAME] Write log entries to a logfile.\n" + " --controlcookie=FILENAME Write control socket cookie to FILENAME.\n" + " --bypass-security Disables meta protocol security, for debugging.\n" ++ " -o [HOST.]KEY=VALUE Set global/host configuration value.\n" + " -R, --chroot chroot to NET dir at startup.\n" + " -U, --user=USER setuid to given USER at startup.\n" " --help Display this help and exit.\n" + " --version Output version information and exit.\n\n"); printf("Report bugs to tinc@tinc-vpn.org.\n"); } } static bool parse_options(int argc, char **argv) { + config_t *cfg; int r; int option_index = 0; + int lineno = 0; - while((r = getopt_long(argc, argv, "c:DLd::n:RU:", long_options, &option_index)) != EOF) { + cmdline_conf = list_alloc((list_action_t)free_config); + - while((r = getopt_long(argc, argv, "c:DLd::k::n:o:K::RU:", long_options, &option_index)) != EOF) { ++ while((r = getopt_long(argc, argv, "c:DLd::n:o:RU:", long_options, &option_index)) != EOF) { switch (r) { case 0: /* long option */ break; @@@ -167,10 -184,71 +175,19 @@@ debug_level++; break; - case 'k': /* kill old tincds */ -#ifndef HAVE_MINGW - if(optarg) { - if(!strcasecmp(optarg, "HUP")) - kill_tincd = SIGHUP; - else if(!strcasecmp(optarg, "TERM")) - kill_tincd = SIGTERM; - else if(!strcasecmp(optarg, "KILL")) - kill_tincd = SIGKILL; - else if(!strcasecmp(optarg, "USR1")) - kill_tincd = SIGUSR1; - else if(!strcasecmp(optarg, "USR2")) - kill_tincd = SIGUSR2; - else if(!strcasecmp(optarg, "WINCH")) - kill_tincd = SIGWINCH; - else if(!strcasecmp(optarg, "INT")) - kill_tincd = SIGINT; - else if(!strcasecmp(optarg, "ALRM")) - kill_tincd = SIGALRM; - else { - kill_tincd = atoi(optarg); - - if(!kill_tincd) { - fprintf(stderr, "Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, USR1, USR2, WINCH, INT or ALRM.\n", - optarg); - usage(true); - return false; - } - } - } else - kill_tincd = SIGTERM; -#else - kill_tincd = 1; -#endif - break; - case 'n': /* net name given */ - netname = xstrdup(optarg); + /* netname "." is special: a "top-level name" */ + netname = strcmp(optarg, ".") != 0 ? + xstrdup(optarg) : NULL; + break; + + case 'o': /* option */ + cfg = parse_config_line(optarg, NULL, ++lineno); + if (!cfg) + return false; + list_insert_tail(cmdline_conf, cfg); break; - case 'K': /* generate public/private keypair */ - if(optarg) { - generate_keys = atoi(optarg); - - if(generate_keys < 512) { - fprintf(stderr, "Invalid argument `%s'; BITS must be a number equal to or greater than 512.\n", - optarg); - usage(true); - return false; - } - - generate_keys &= ~7; /* Round it to bytes */ - } else - generate_keys = 2048; - break; - case 'R': /* chroot to NETNAME dir */ do_chroot = true; break;