Author: Kev <klmitch@mit.edu>
authorKevin L. Mitchell <klmitch@mit.edu>
Mon, 18 Dec 2000 21:24:00 +0000 (21:24 +0000)
committerKevin L. Mitchell <klmitch@mit.edu>
Mon, 18 Dec 2000 21:24:00 +0000 (21:24 +0000)
Log message:

Rearranged a couple of features to try to make things a little neater;
rearranged some code to make it more streamlined.

I also created a subdirectory with API documentation; this is for
documentation of special little subsystems, such as the features subsystem
or the logging system, or even something like modebuf's and MsgQ's.  Please
read doc/api/api.txt and tell me what you think...

Testing:

The features changes compile and link, but I have not performed run-time
tests.  The documentation that I have written should be mostly complete,
but let me know if you want more information...

git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@349 c9e4aea6-c8fd-4c43-8297-357d70d61c8c

ChangeLog
doc/api/api.txt [new file with mode: 0644]
doc/api/features.txt [new file with mode: 0644]
doc/api/log.txt [new file with mode: 0644]
include/ircd_features.h
ircd/ircd_features.c

index e9d58e4297a311e8ce579dacb6e3aad1e14735a3..df1ad9a53c1ab4b3628e71c45ec6ac6e494ea3e4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2000-12-18  Kevin L. Mitchell  <klmitch@mit.edu>
+
+       * doc/api/log.txt: how to use the logging API
+
+       * doc/api/features.txt: how to use the features API
+
+       * doc/api/api.txt: how to write API documentation
+
+       * include/ircd_features.h: rearranged a couple of features for
+       neatness purposes
+
+       * ircd/ircd_features.c: cleaned up the macros some; rearranged
+       some code to all go into the switch; rearranged a couple of
+       features for neatness purposes
+
 2000-12-16  Greg Sikorski <gte@atomicrevs.demon.co.uk>
        * ircd/os_bsd.c: Added os_set_tos for BSD users.
 
diff --git a/doc/api/api.txt b/doc/api/api.txt
new file mode 100644 (file)
index 0000000..5dc9993
--- /dev/null
@@ -0,0 +1,105 @@
+This directory is intended for documents describing programming
+interfaces within ircu, including such things as modebuf's and the
+features interface.  Please write these documents as plain text; if we
+want HTML, we can write a script to convert the text versions into
+HTML versions.  Toward that end, I respectfully suggest everyone
+conform to a common format, which I will describe here:
+
+Every .txt file should begin with a couple of paragraphs giving an
+overview of the API, its purpose, and how to use it.  Paragraphs
+should be separated by blank lines, as shown here.  Paragraphs that do
+not end in some form of punctuation, such as a period, will be treated
+as section headings.  The introduction ends when the first API element
+appears.  API element documentation is introduced with "<" followed by
+the element type--"struct", "typedef", "function", "macro", or (heaven
+forbid) "global", followed by ">", all on a line by itself.  The next
+line should contain a declaration of the element as it would appear in
+a header file; this may spread across multiple lines and contain
+comments and blank lines.  The declaration ends for most elements when
+a ";" is encountered; for macros, the declaration ends on the last
+line not ending in "\".
+
+The documentation for the API element should immediately follow the
+declaration of that element, and should be separated from it by a
+single blank line.  This documentation should explain the purpose of
+the element and describe what each of its fields mean.  The
+documentation ends when the corresponding "</" tag is reached, just as
+in HTML or XML.  (I don't intend for the files to be either HTML or
+XML, I just want them to be easy to parse so they could be turned into
+either, as occasion warrants.)  An example follows:
+
+<struct>
+struct FooBar; /* a sample structure with no definition */
+
+The comment, since it's on the same line as the ";", is associated
+with the declaration for struct FooBar.
+</struct>
+
+<struct>
+struct FooBar {
+  long     fb_magic;   /* a magic number */
+  char    *fb_string;  /* a string of some sort */
+};
+
+The sequence "};" ends the struct declaration.
+</struct>
+
+<typedef>
+typedef FooBar_t;      /* a simple typedef */
+
+This element shows how to hide the inner workings of typedefs.
+</typedef>
+
+<typedef>
+typedef struct FooBar FooBar_t;        /* a more complex typedef */
+
+Here we show the full typedef declaration.
+</typedef>
+
+<global>
+extern int fooBarFreeList;     /* global variables should be avoided */
+
+You should avoid global variables, but if you must have one for alloc
+counts or whatever, here's how to specify documentation for them.
+</global>
+
+<macro>
+#define HAVE_FOOBAR            /* We have FOOBAR, whatever it may be */
+
+This could be used for boolean macros (macros used in #ifdef's, for
+instance) or for simple value macros where you're hiding the values.
+Since there are so many variations on macros, I'll only show one other
+variation below:
+</macro>
+
+<macro>
+#define FooBarVerify(foobar)   ((foobar) && \
+                                (foobar)->fb_magic == FOOBAR_STRUCT_MAGIC)
+
+This macro takes arguments.  Again, we could leave out the actual
+definition, or even treat the macro as a function rather than a
+macro.  This also shows how to do multi-line macros.
+</macro>
+
+<function>
+void *foobar(struct FooBar *blah, int flag);
+
+Since function definitions never appear in header files anyway, we
+don't have to worry about hiding information.  You should leave off
+"extern" in the function declaration, and please include names for the
+variables, so you can refer to them in the function documentation.
+</function>
+
+The API document may then end in some summary information, if you
+wish, or a ChangeLog of some form, such as follows.
+
+<authors>
+Kev <klmitch@mit.edu>
+</authors>
+
+<changelog>
+[2000-12-18 Kev] Initial definition of how API documents should look.
+Further entries in the changelog should *precede* this one and should
+be separated from it by a blank line.  Also specify your name, as
+listed in the "<authors>" section, so we know who to blame ;)
+</changelog>
diff --git a/doc/api/features.txt b/doc/api/features.txt
new file mode 100644 (file)
index 0000000..d388540
--- /dev/null
@@ -0,0 +1,203 @@
+As of u2.10.11, most of the compile-time configuration options present
+in previous versions of ircu have been provided via the configuration
+file as "features."  This document is intended not only to give an
+explanation of how to use the features subsystem in new code, but also
+how to define new features.
+
+In the ircd_features.h header file is an enum Feature that lists all
+the features known to the features subsystem.  The order of entries in
+this list must match precisely the order of features as listed in the
+features[] table in ircd_features.c.  There are four kinds of
+features, five different flags that can be set for features, and six
+different call-backs for more complex features.
+
+Types of Features
+
+There are at present four different types of features: NONE, INT,
+BOOL, and STR.  Features of type "NONE" are complex features, such as
+the logging subsystem, that have complicated behavior that's managed
+through the use of call-backs.  The call-backs available are set,
+which is called to set the value of the feature; reset, which is
+called to reset the value of the feature back to its default; get,
+which is called to send the user a RPL_FEATURE to describe the feature
+setting; unmark, which is called prior to reading the configuration
+file; mark, which is called after reading the configuration file; and
+report, which is used to send a user a list of RPL_STATSFLINE
+replies.
+
+In comparison to type "NONE," the other types are very simple.  Type
+"INT" is used for features that take an integer value; "BOOL" is for
+those features that are boolean types; and "STR" is for those features
+that take simple string values.  The values for these feature types
+are handled directly by the features subsystem, and can be examined
+from code with the feature_int(), feature_bool(), and feature_str()
+functions, described below.
+
+Feature Flags
+
+There are five feature flags, one of which is used internally by the
+feature subsystem.  Two of these flags, FEAT_OPER and FEAT_MYOPER, are
+used to select who can see the settings of those features; FEAT_OPER
+permits any operator anywhere on the network to examine the settings
+of a particular feature, whereas FEAT_MYOPER only permits operators
+local to a server to examine feature values.  If neither of these two
+flags is specified, then any user may examine that feature's value.
+
+The other two flags only have any meaning for string values; they are
+FEAT_NULL, which is used to specify that a feature of type "STR" may
+have a NULL value, and FEAT_CASE, which specifies that the feature is
+case sensitive--this may be used on file names, for example.
+
+Marking Features
+
+When the configuration file is read, there must be some way to
+determine if a particular F-line has been removed since the last time
+the configuration file was read.  The way this is done in the features
+subsystem is to have a "mark" for each feature.  Prior to reading the
+configuration file, all marks are cleared for all features (and all
+"unmark" call-backs are called).  As each F-line is encountered and
+processed, that feature's mark is set.  Finally, when the
+configuration file has been fully read, all remaining unmarked
+features are reset to their default values (and all "mark" call-backs
+are called).
+
+Adding New Features
+
+To add a new feature, first determine the feature's name (which must
+begin with the string "FEAT_") and its type ("NONE," "INT," "BOOL," or
+"STR").  Then add the feature to the enum Feature in an appropriate
+place (i.e., it's good to group all features affecting operators
+separate from those features affecting networking code), and a
+corresponding entry in the features[] table in ircd_features.c.  It
+will be best to use one of the F_?() macros, which are documented
+below.  Then, whenever you need to refer to the value of a specific
+feature, call the appropriate feature_<type>() function, as documented
+below.
+
+<enum>
+enum Feature;
+
+The "Feature" enum lists all of the features known to the feature
+subsystem.  Each feature name *must* begin with "FEAT_"; the portion
+of the name following "FEAT_" will be what you use to set the feature
+from the configuration file or with the "set" or "reset" commands.
+</enum>
+
+<function>
+int feature_set(struct Client* from, const char* const* fields, int count);
+
+The feature_set() function takes an array of strings and a count of
+the number of strings in the array.  The first string is a feature
+name, and, for most features, the second string will be that feature's
+value.  The _from_ parameter is the struct Client describing the user
+that issued the "set" command.  This parameter may be NULL if
+feature_set() is being called from the configuration file subsystem.
+</function>
+
+<function>
+int feature_reset(struct Client* from, const char* const* fields, int count);
+
+The feature_reset() function is very similar in arguments to the
+feature_set() function, except that it may not be called from the
+configuration file subsystem.  It resets the named feature to its
+default value.
+</function>
+
+<function>
+int feature_get(struct Client* from, const char* const* fields, int count);
+
+Again, feature_get() is very similar in arguments to the feature_set()
+function, except that again it may not be called from the
+configuration file subsystem.  It reports the value of the named
+feature to the user that issued the "get" command.
+</function>
+
+<function>
+void feature_unmark(void);
+
+This function is used to unmark all feature values, as described in
+the subsection "Marking Features."  It takes no arguments and returns
+nothing.
+</function>
+
+<function>
+void feature_mark(void);
+
+The complement to feature_unmark(), feature_mark() resets all
+unchanged feature settings to their defaults.  See the subsection on
+"Marking Features."
+</function>
+
+<function>
+void feature_report(struct Client* to);
+
+Reports all F-lines to a user using RPL_STATSFLINE, except those which
+the user is not permitted to see due to flag settings.
+</function>
+
+<function>
+int feature_int(enum Feature feat);
+
+To retrieve the values of integer features, call this function.
+Calling this function on a different type of feature, such as a "BOOL"
+feature, will result in an assertion failure.
+</function>
+
+<function>
+int feature_bool(enum Feature feat);
+
+This function is the complement of feature_int() for features of type
+"BOOL."
+</function>
+
+<function>
+const char *feature_str(enum Feature feat);
+
+Use this function to retrieve strings values for features of type
+"STR"; you may not modify nor free the string value.
+</function>
+
+<macro>
+#define F_N(type, flags, set, reset, get, unmark, mark, report)
+
+This macro is used in the features[] table to simplify defining a
+feature of type "NONE."  The _type_ parameter is the name of the
+feature excluding the "FEAT_" prefix, and MUST NOT be in
+double-quotes.  The _flags_ parameter may be 0, FEAT_OPER, or
+FEAT_MYOPER--the bitwise OR of these two flags is permissible but
+would not make sense.  The rest of the arguments are pointers to
+functions implementing the named call-back.
+</macro>
+
+<macro>
+#define F_I(type, flags, v_int)
+
+To define integer features, use the F_I() macro.  The _type_ and
+_flags_ parameters are as for F_N(), and the _v_int_ macro specifies
+the default value of the feature.
+</macro>
+
+<macro>
+#define F_B(type, flags, v_int)
+
+This macro is used for defining features of type "BOOL"; it is very
+similar to F_I(), but _v_int_ should either 0 (for a "FALSE" value) or
+1 (for a "TRUE" value).
+</macro>
+
+<macro>
+#define F_S(type, flags, v_str)
+
+Also similar to F_I(), F_S() defines features of type "STR."  The
+_flags_ argument may be the bitwise OR of one of FEAT_OPER or
+FEAT_MYOPER with the special string flags FEAT_NULL and FEAT_CASE,
+which are described above in the section "Feature Flags."
+</macro>
+
+<authors>
+Kev <klmitch@mit.edu>
+</authors>
+
+<changelog>
+[2000-12-18 Kev] Document the features API
+</changelog>
diff --git a/doc/api/log.txt b/doc/api/log.txt
new file mode 100644 (file)
index 0000000..8f46d52
--- /dev/null
@@ -0,0 +1,238 @@
+Old versions of ircu did not have very good means of dealing with
+logging.  In u2.10.11, an entirely new logging subsystem was written,
+allowing a server administrator much more power in determining what
+actions are to be logged where.  The new logging subsystem permits log
+messages to go to syslog, to a file, and to server operators via
+server notices, simultaneously (though having output to multiple log
+files is not presently supported).
+
+All log messages have two values that are passed in with them: the
+logging level, which must be one of the values in enum LogLevel, and a
+logging subsystem, which must be one of the values in enum LogSys;
+these values are used as indexes into arrays within ircd_log.c, so be
+careful should you change them.
+
+In addition to the LogLevel and LogSys, there is also a set of three
+flags that may be passed to the log_write() logging function; these
+flags may be used to suppress certain types of logging that may be
+undesirable.  For instance, when a server links, a log may be written
+containing the server's IP address; to prevent this IP address from
+ever showing up in a server notice, that invocation of log_write() is
+passed the LOG_NOSNOTICE flag.
+
+<enum>
+enum LogLevel {
+  L_CRIT,
+  L_ERROR,
+  L_WARNING,
+  L_NOTICE,
+  L_TRACE,
+  L_INFO,
+  L_DEBUG,
+  L_LAST_LEVEL
+};
+
+This enum describes the severity levels of a log message.  The
+severity decreases as you proceed upwards in the list, so L_DEBUG is
+less severe than L_INFO, and L_CRIT in the most severe of all.  The
+special value L_LAST_LEVEL should never be used; it merely marks the
+end of the list.
+</enum>
+
+<enum>
+enum LogSys {
+  LS_SYSTEM, LS_CONFIG, LS_OPERMODE, LS_GLINE, LS_JUPE, LS_WHO, LS_NETWORK,
+  LS_OPERKILL, LS_SERVKILL, LS_USER, LS_OPER, LS_RESOLVER, LS_SOCKET,
+  LS_DEBUG, LS_OLDLOG,
+  LS_LAST_SYSTEM
+};
+
+These are the various logging subsystems recognized by the logging
+subsystem.  Again, order is important, and again, LS_LAST_SYSTEM
+should never be used.
+</enum>
+
+<function>
+void log_debug_init(int usetty);
+
+This initializes the special-purpose debug logging code in the
+server.  If the _usetty_ parameter is non-zero, then all debugging
+output will go to the terminal regardless of file settings for the
+LS_DEBUG subsystem.  This function is not defined unless the server is
+compiled with -DDEBUGMODE.
+</function>
+
+<function>
+void log_init(const char *process_name);
+
+This initializes the entire logging subsystem, including special
+things such as storing the process name and opening syslog with the
+open_log() function.  It may only be called once.
+</function>
+
+<function>
+void log_reopen(void);
+
+All log files are persistently open, in order to avoid the overhead of
+re-opening the log file each time.  This function is used to close all
+the log files and to close and reopen syslog.  (Log files are opened
+again only when there is something to write to them.)
+</function>
+
+<function>
+void log_close(void);
+
+This closes all log files and the syslog prior to the server
+terminating.  Should logs need to be reopened after calling this
+function, call log_reopen() instead of log_init().
+</function>
+
+<function>
+void log_write(enum LogSys subsys, enum LogLevel severity,
+              unsigned int flags, const char *fmt, ...);
+
+This is the actual logging function.  The _flags_ parameter is 0 or
+the bitwise OR of LOG_NOSYSLOG (suppresses syslogging), LOG_NOFILELOG
+(suppresses logging to a file) and LOG_NOSNOTICE (suppresses logging
+via server notices).  The _fmt_ parameter is a format string
+acceptable to ircd_snprintf(), which is the function called to
+actually format the log message.
+</function>
+
+<function>
+void log_vwrite(enum LogSys subsys, enum LogLevel severity,
+               unsigned int flags, const char *fmt, va_list vl);
+
+This is similar to log_write() except that it takes a va_list
+parameter.
+</function>
+
+<function>
+char *log_cannon(const char *subsys);
+
+This returns the canonical name for logging subsystem.  This probably
+should not be exposed here, but it is needed in ircd_features.c at
+present.
+</function>
+
+<function>
+int log_set_file(const char *subsys, const char *filename);
+
+This sets the file name for the specified logging subsystem to
+_filename_; returns 2 if the subsystem was undefined, 1 if the value
+of _filename_ was not understood, or 0 if there was no error.
+</function>
+
+<function>
+char *log_get_file(const char *subsys);
+
+This returns the current log file name for the given subsystem.
+</function>
+
+<function>
+int log_set_facility(const char *subsys, const char *facility);
+
+This sets the syslog facility for the specified logging subsystem to
+_facility_; returns 2 if the subsystem was undefined, 1 if the value
+of _facility_ was not understood, or 0 if there was no error.  Two
+special facility names may be given; "NONE" specifies that no
+syslogging should be performed, and "DEFAULT" specifies that ircd's
+default syslog facility should be used.
+</function>
+
+<function>
+char *log_get_facility(const char *subsys);
+
+This returns the current syslog facility for the given subsystem.  See
+the documentation for log_set_facility() for a description of the
+special facility names "NONE" and "DEFAULT."
+</function>
+
+<function>
+int log_set_snomask(const char *subsys, const char *snomask);
+
+This sets the server notice type for the specified logging subsystem
+to _snomask_; returns 2 if the subsystem was undefined, 1 if the value
+of _snomask_ was not understood, or 0 if there was no error.  The
+special server notice type "NONE" indicates that no server notices
+should be generated.  The other valid values for _snomask_ are:
+"OLDSNO," "SERVKILL," "OPERKILL," "HACK2," "HACK3," "UNAUTH,"
+"TCPCOMMON," "TOOMANY," "HACK4," "GLINE," "NETWORK," "IPMISMATCH,"
+"THROTTLE," "OLDREALOP," "CONNEXIT," and "DEBUG."
+</function>
+
+<function>
+char *log_get_snomask(const char *subsys);
+
+This returns the current server notice type for the given subsystem.
+See the documentation for log_set_snomask() for a description of the
+return values.
+</function>
+
+<function>
+int log_set_level(const char *subsys, const char *level);
+
+This function is used to set the minimum log level for a particular
+subsystem; returns 2 if the subsystem was undefined, 1 if the value of
+_level_ was not understood, or 0 if there was no error.  Any log
+notices generated with lower severity than that set with this function
+will not be logged.  Valid values are "CRIT," "ERROR," "WARNING,"
+"NOTICE," "TRACE," "INFO," and "DEBUG."
+</function>
+
+<function>
+char *log_get_level(const char *subsys);
+
+This returns the current minimum log level for the given subsystem.
+See the documentation for log_set_level() for a description of the
+return values.
+</function>
+
+<function>
+int log_set_default(const char *facility);
+
+This function sets the default syslog facility for all of ircd.  Valid
+values for _facility_ are as described for log_set_facility() with the
+exclusion of the "NONE" and "DEFAULT" facilities; returns 1 if the
+facility name was unrecognized (or proscribed) or 0 if there was no
+error.
+</function>
+
+<function>
+char *log_get_default(void);
+
+This simply returns ircd's default syslog facility.
+</function>
+
+<function>
+void log_feature_unmark(void);
+
+This function is called by the ircd_features.c subsystem and should
+not be called by any other part of ircd.  See the features API
+documentation for notes on what this function does.
+</function>
+
+<function>
+void log_feature_mark(int flag);
+
+This function is called by the ircd_features.c subsystem and should
+not be called by any other part of ircd.  See the features API
+documentation for notes on what this function does.
+</function>
+
+<function>
+void log_feature_report(struct Client *to, int flag);
+
+This function is called by the ircd_features.c subsystem and should
+not be called by any other part of ircd.  See the features API
+documentation for notes on what this function does.
+</function>
+
+<authors>
+Kev <klmitch@mit.edu>
+</authors>
+
+<changelog>
+[2000-12-18 Kev] Wrote some documentation on how to use the logging
+subsystem.
+</changelog>
index dd72ea7ebbf4323653306a059df9932a87d83277..758bf5b6597860d73ca062e8305c1430985e89ea 100644 (file)
@@ -26,6 +26,11 @@ struct Client;
 enum Feature {
   FEAT_LOG,
 
+  /* Networking features */
+  FEAT_TOS_SERVER,
+  FEAT_TOS_CLIENT,
+
+  /* features that affect all operators */
   FEAT_OPER_NO_CHAN_LIMIT,
   FEAT_OPER_MODE_LCHAN,
   FEAT_OPER_WALK_THROUGH_LMODES,
@@ -34,10 +39,9 @@ enum Feature {
   FEAT_SHOW_ALL_INVISIBLE_USERS,
   FEAT_UNLIMIT_OPER_QUERY,
   FEAT_LOCAL_KILL_ONLY,
-  FEAT_TOS_SERVER,
-  FEAT_TOS_CLIENT,
   FEAT_CONFIG_OPERCMDS,
 
+  /* features that affect global opers on this server */
   FEAT_OPER_KILL,
   FEAT_OPER_REHASH,
   FEAT_OPER_RESTART,
@@ -53,6 +57,7 @@ enum Feature {
   FEAT_OPER_SET,
   FEAT_OPERS_SEE_IN_SECRET_CHANNELS,
 
+  /* features that affect local opers on this server */
   FEAT_LOCOP_KILL,
   FEAT_LOCOP_REHASH,
   FEAT_LOCOP_RESTART,
index 4dd8d5d454fe501592a59531afc399a4bf8b49c1..5c235383551b0a674ca4ed2a62efe80e12e750ef 100644 (file)
@@ -204,8 +204,8 @@ static struct FeatureDesc {
   feat_mark_call   mark;    /* reset to defaults all unchanged features */
   feat_report_call report;  /* report feature values */
 } features[] = {
-#define F(type, flags, v_int, v_str, set, reset, get, unmark, mark, report)   \
-  { FEAT_ ## type, #type, (flags), 0, (v_int), 0, (v_str),                   \
+#define F_N(type, flags, set, reset, get, unmark, mark, report)                      \
+  { FEAT_ ## type, #type, FEAT_NONE | (flags), 0, 0, 0, 0,                   \
     (set), (reset), (get), (unmark), (mark), (report) }
 #define F_I(type, flags, v_int)                                                      \
   { FEAT_ ## type, #type, FEAT_INT | (flags), 0, (v_int), 0, 0,                      \
@@ -213,14 +213,18 @@ static struct FeatureDesc {
 #define F_B(type, flags, v_int)                                                      \
   { FEAT_ ## type, #type, FEAT_BOOL | (flags), 0, (v_int), 0, 0,             \
     0, 0, 0, 0, 0, 0 }
-#define F_S(type, flags, v_int)                                                      \
+#define F_S(type, flags, v_str)                                                      \
   { FEAT_ ## type, #type, FEAT_STR | (flags), 0, 0, 0, (v_str),                      \
     0, 0, 0, 0, 0, 0 }
 
-  F(LOG, FEAT_NONE | FEAT_MYOPER, 0, 0,
-    feature_log_set, feature_log_reset, feature_log_get,
-    log_feature_unmark, log_feature_mark, log_feature_report),
+  F_N(LOG, FEAT_MYOPER, feature_log_set, feature_log_reset, feature_log_get,
+      log_feature_unmark, log_feature_mark, log_feature_report),
 
+  /* Networking features */
+  F_I(TOS_SERVER, 0, 0x08),
+  F_I(TOS_CLIENT, 0, 0x08),
+
+  /* features that affect all operators */
   F_B(OPER_NO_CHAN_LIMIT, 0, 1),
   F_B(OPER_MODE_LCHAN, 0, 1),
   F_B(OPER_WALK_THROUGH_LMODES, 0, 0),
@@ -229,10 +233,9 @@ static struct FeatureDesc {
   F_B(SHOW_ALL_INVISIBLE_USERS, 0, 1),
   F_B(UNLIMIT_OPER_QUERY, 0, 0),
   F_B(LOCAL_KILL_ONLY, 0, 0),
-  F_I(TOS_SERVER, 0, 0x08),
-  F_I(TOS_CLIENT, 0, 0x08),
   F_B(CONFIG_OPERCMDS, 0, 1), /* XXX change default before release */
 
+  /* features that affect global opers on this server */
   F_B(OPER_KILL, 0, 1),
   F_B(OPER_REHASH, 0, 1),
   F_B(OPER_RESTART, 0, 1),
@@ -248,6 +251,7 @@ static struct FeatureDesc {
   F_B(OPER_SET, 0, 1),
   F_B(OPERS_SEE_IN_SECRET_CHANNELS, 0, 1),
 
+  /* features that affect local opers on this server */
   F_B(LOCOP_KILL, 0, 0),
   F_B(LOCOP_REHASH, 0, 1),
   F_B(LOCOP_RESTART, 0, 0),
@@ -262,7 +266,7 @@ static struct FeatureDesc {
 #undef F_S
 #undef F_B
 #undef F_I
-#undef F
+#undef F_N
   { FEAT_LAST_F, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
 };
 
@@ -303,90 +307,93 @@ feature_set(struct Client* from, const char* const* fields, int count)
     else
       log_write(LS_CONFIG, L_ERROR, 0, "Not enough fields in F line");
   } else if ((feat = feature_desc(from, fields[0]))) { /* find feature */
-    if (feat->set && (i = (*feat->set)(from, fields + 1, count - 1))) {
-      if (i > 0) /* call the set callback and do marking */
-       feat->flags |= FEAT_MARK;
-      else /* i < 0 */
-       feat->flags &= ~FEAT_MARK;
-    } else /* Ok, it's a value we can fiddle with */
-      switch (feat->flags & FEAT_MASK) {
-      case FEAT_INT: /* an integer value */
-       if (count < 2) { /* reset value */
-         feat->v_int = feat->def_int;
+    switch (feat->flags & FEAT_MASK) {
+    case FEAT_NONE:
+      if (feat->set && (i = (*feat->set)(from, fields + 1, count - 1))) {
+       if (i > 0) /* call the set callback and do marking */
+         feat->flags |= FEAT_MARK;
+       else /* i < 0 */
          feat->flags &= ~FEAT_MARK;
-       } else { /* ok, figure out the value and whether to mark it */
-         feat->v_int = atoi(fields[1]);
-         if (feat->v_int == feat->def_int)
-           feat->flags &= ~FEAT_MARK;
-         else
-           feat->flags |= FEAT_MARK;
-       }
        break;
+      }
 
-      case FEAT_BOOL: /* it's a boolean value--true or false */
-       if (count < 2) { /* reset value */
-         feat->v_int = feat->def_int;
+    case FEAT_INT: /* an integer value */
+      if (count < 2) { /* reset value */
+       feat->v_int = feat->def_int;
+       feat->flags &= ~FEAT_MARK;
+      } else { /* ok, figure out the value and whether to mark it */
+       feat->v_int = atoi(fields[1]);
+       if (feat->v_int == feat->def_int)
          feat->flags &= ~FEAT_MARK;
-       } else { /* figure out the value and whether to mark it */
-         if (!ircd_strncmp(fields[1], "TRUE", strlen(fields[1])) ||
-             !ircd_strncmp(fields[1], "YES", strlen(fields[1])) ||
-             (strlen(fields[1]) >= 2 &&
-              !ircd_strncmp(fields[1], "ON", strlen(fields[1]))))
-           feat->v_int = 1;
-         else if (!ircd_strncmp(fields[1], "FALSE", strlen(fields[1])) ||
-                  !ircd_strncmp(fields[1], "NO", strlen(fields[1])) ||
-                  (strlen(fields[1]) >= 2 &&
-                   !ircd_strncmp(fields[1], "OFF", strlen(fields[1]))))
-           feat->v_int = 0;
-         else if (from) /* report an error... */
-           return send_reply(from, ERR_BADFEATVALUE, fields[1], feat->type);
-         else {
-           log_write(LS_CONFIG, L_ERROR, 0, "Bad value \"%s\" for feature %s",
-                     fields[1], feat->type);
-           return 0;
-         }
+       else
+         feat->flags |= FEAT_MARK;
+      }
+      break;
 
-         if (feat->v_int == feat->def_int) /* figure out whether to mark it */
-           feat->flags &= ~FEAT_MARK;
-         else
-           feat->flags |= FEAT_MARK;
+    case FEAT_BOOL: /* it's a boolean value--true or false */
+      if (count < 2) { /* reset value */
+       feat->v_int = feat->def_int;
+       feat->flags &= ~FEAT_MARK;
+      } else { /* figure out the value and whether to mark it */
+       if (!ircd_strncmp(fields[1], "TRUE", strlen(fields[1])) ||
+           !ircd_strncmp(fields[1], "YES", strlen(fields[1])) ||
+           (strlen(fields[1]) >= 2 &&
+            !ircd_strncmp(fields[1], "ON", strlen(fields[1]))))
+         feat->v_int = 1;
+       else if (!ircd_strncmp(fields[1], "FALSE", strlen(fields[1])) ||
+                !ircd_strncmp(fields[1], "NO", strlen(fields[1])) ||
+                (strlen(fields[1]) >= 2 &&
+                 !ircd_strncmp(fields[1], "OFF", strlen(fields[1]))))
+         feat->v_int = 0;
+       else if (from) /* report an error... */
+         return send_reply(from, ERR_BADFEATVALUE, fields[1], feat->type);
+       else {
+         log_write(LS_CONFIG, L_ERROR, 0, "Bad value \"%s\" for feature %s",
+                   fields[1], feat->type);
+         return 0;
        }
-       break;
 
-      case FEAT_STR: /* it's a string value */
-       if (count < 2 ||
-           !(feat->flags & FEAT_CASE ? strcmp(fields[1], feat->def_str) :
-             ircd_strcmp(fields[1], feat->def_str))) { /* reset to default */
-         if (feat->v_str && feat->v_str != feat->def_str)
-           MyFree(feat->v_str); /* free old value */
-         feat->v_str = feat->def_str; /* very special... */
-
-         feat->flags &= ~FEAT_MARK; /* unmark it */
-       } else {
-         if (!*fields[1]) { /* empty string translates to NULL */
-           if (feat->flags & FEAT_NULL) { /* permitted? */
-             if (feat->v_str && feat->v_str != feat->def_str)
-               MyFree(feat->v_str); /* free old value */
-             feat->v_str = 0; /* set it to NULL */
-           } else if (from) /* hmmm...not permitted; report error */
-             return send_reply(from, ERR_BADFEATVALUE, "NULL", feat->type);
-           else {
-             log_write(LS_CONFIG, L_ERROR, 0,
-                       "Bad value \"NULL\" for feature %s", feat->type);
-             return 0;
-           }
-         } else if ((feat->flags & FEAT_CASE ?
-                     strcmp(fields[1], feat->v_str) :
-                     ircd_strcmp(fields[1], feat->v_str))) { /* new value */
+       if (feat->v_int == feat->def_int) /* figure out whether to mark it */
+         feat->flags &= ~FEAT_MARK;
+       else
+         feat->flags |= FEAT_MARK;
+      }
+      break;
+
+    case FEAT_STR: /* it's a string value */
+      if (count < 2 ||
+         !(feat->flags & FEAT_CASE ? strcmp(fields[1], feat->def_str) :
+           ircd_strcmp(fields[1], feat->def_str))) { /* reset to default */
+       if (feat->v_str && feat->v_str != feat->def_str)
+         MyFree(feat->v_str); /* free old value */
+       feat->v_str = feat->def_str; /* very special... */
+
+       feat->flags &= ~FEAT_MARK; /* unmark it */
+      } else {
+       if (!*fields[1]) { /* empty string translates to NULL */
+         if (feat->flags & FEAT_NULL) { /* permitted? */
            if (feat->v_str && feat->v_str != feat->def_str)
              MyFree(feat->v_str); /* free old value */
-           DupString(feat->v_str, fields[1]); /* store new value */
+           feat->v_str = 0; /* set it to NULL */
+         } else if (from) /* hmmm...not permitted; report error */
+           return send_reply(from, ERR_BADFEATVALUE, "NULL", feat->type);
+         else {
+           log_write(LS_CONFIG, L_ERROR, 0,
+                     "Bad value \"NULL\" for feature %s", feat->type);
+           return 0;
          }
-
-         feat->flags |= FEAT_MARK; /* mark it as having been touched */
+       } else if ((feat->flags & FEAT_CASE ?
+                   strcmp(fields[1], feat->v_str) :
+                   ircd_strcmp(fields[1], feat->v_str))) { /* new value */
+         if (feat->v_str && feat->v_str != feat->def_str)
+           MyFree(feat->v_str); /* free old value */
+         DupString(feat->v_str, fields[1]); /* store new value */
        }
-       break;
+
+       feat->flags |= FEAT_MARK; /* mark it as having been touched */
       }
+      break;
+    }
   }
 
   return 0;
@@ -407,26 +414,28 @@ feature_reset(struct Client* from, const char* const* fields, int count)
   if (count < 1) /* check arguments */
     need_more_params(from, "RESET");
   else if ((feat = feature_desc(from, fields[0]))) { /* get descriptor */
-    if (feat->reset && (i = (*feat->reset)(from, fields + 1, count - 1))) {
-      if (i > 0) /* call reset callback and parse mark return */
-       feat->flags |= FEAT_MARK;
-      else /* i < 0 */
-       feat->flags &= ~FEAT_MARK;
-    } else { /* oh, it's something we own... */
-      switch (feat->flags & FEAT_MASK) {
-      case FEAT_INT:  /* Integer... */
-      case FEAT_BOOL: /* Boolean... */
-       feat->v_int = feat->def_int; /* set the default */
-       break;
-
-      case FEAT_STR: /* string! */
-       if (feat->v_str && feat->v_str != feat->def_str)
-         MyFree(feat->v_str); /* free old value */
-       feat->v_str = feat->def_str; /* set it to default */
-       break;
+    switch (feat->flags & FEAT_MASK) {
+    case FEAT_NONE: /* None... */
+      if (feat->reset && (i = (*feat->reset)(from, fields + 1, count - 1))) {
+       if (i > 0) /* call reset callback and parse mark return */
+         feat->flags |= FEAT_MARK;
+       else /* i < 0 */
+         feat->flags &= ~FEAT_MARK;
       }
+      break;
+
+    case FEAT_INT:  /* Integer... */
+    case FEAT_BOOL: /* Boolean... */
+      feat->v_int = feat->def_int; /* set the default */
+      feat->flags &= ~FEAT_MARK; /* unmark it */
+      break;
 
+    case FEAT_STR: /* string! */
+      if (feat->v_str && feat->v_str != feat->def_str)
+       MyFree(feat->v_str); /* free old value */
+      feat->v_str = feat->def_str; /* set it to default */
       feat->flags &= ~FEAT_MARK; /* unmark it */
+      break;
     }
   }
 
@@ -448,30 +457,32 @@ feature_get(struct Client* from, const char* const* fields, int count)
        (feat->flags & FEAT_OPER && !IsAnOper(from))) /* check privs */
       return send_reply(from, ERR_NOPRIVILEGES);
 
-    if (feat->get) /* if there's a callback, use it */
-      (*feat->get)(from, fields + 1, count - 1);
-    else /* something we own */
-      switch (feat->flags & FEAT_MASK) {
-      case FEAT_INT: /* integer, report integer value */
-       send_reply(from, SND_EXPLICIT | RPL_FEATURE,
-                  ":Integer value of %s: %d", feat->type, feat->v_int);
-       break;
+    switch (feat->flags & FEAT_MASK) {
+    case FEAT_NONE: /* none, call the callback... */
+      if (feat->get) /* if there's a callback, use it */
+       (*feat->get)(from, fields + 1, count - 1);
+      break;
 
-      case FEAT_BOOL: /* boolean, report boolean value */
-       send_reply(from, SND_EXPLICIT | RPL_FEATURE,
-                  ":Boolean value of %s: %s", feat->type,
-                  feat->v_int ? "TRUE" : "FALSE");
-       break;
+    case FEAT_INT: /* integer, report integer value */
+      send_reply(from, SND_EXPLICIT | RPL_FEATURE,
+                ":Integer value of %s: %d", feat->type, feat->v_int);
+      break;
 
-      case FEAT_STR: /* string, report string value */
-       if (feat->v_str) /* deal with null case */
-         send_reply(from, SND_EXPLICIT | RPL_FEATURE,
-                    ":String value of %s: %s", feat->type, feat->v_str);
-       else
-         send_reply(from, SND_EXPLICIT | RPL_FEATURE,
-                    ":String value for %s not set", feat->type);
-       break;
-      }
+    case FEAT_BOOL: /* boolean, report boolean value */
+      send_reply(from, SND_EXPLICIT | RPL_FEATURE,
+                ":Boolean value of %s: %s", feat->type,
+                feat->v_int ? "TRUE" : "FALSE");
+      break;
+
+    case FEAT_STR: /* string, report string value */
+      if (feat->v_str) /* deal with null case */
+       send_reply(from, SND_EXPLICIT | RPL_FEATURE,
+                  ":String value of %s: %s", feat->type, feat->v_str);
+      else
+       send_reply(from, SND_EXPLICIT | RPL_FEATURE,
+                  ":String value for %s not set", feat->type);
+      break;
+    }
   }
 
   return 0;
@@ -496,25 +507,27 @@ feature_mark(void)
 {
   int i;
 
-  for (i = 0; features[i].type; i++) {
-    if (!(features[i].flags & FEAT_MARK)) { /* not changed? */
-      switch (features[i].flags & FEAT_MASK) {
-      case FEAT_INT:  /* Integers or Booleans... */
-      case FEAT_BOOL:
+  for (i = 0; features[i].type; i++)
+    switch (features[i].flags & FEAT_MASK) {
+    case FEAT_NONE:
+      if (features[i].mark) /* call the mark callback if necessary */
+       (*features[i].mark)(features[i].flags & FEAT_MARK ? 1 : 0);
+      break;
+
+    case FEAT_INT:  /* Integers or Booleans... */
+    case FEAT_BOOL:
+      if (!(features[i].flags & FEAT_MARK)) /* not changed? */
        features[i].v_int = features[i].def_int;
-       break;
+      break;
 
-      case FEAT_STR: /* strings... */
+    case FEAT_STR: /* strings... */
+      if (!(features[i].flags & FEAT_MARK)) { /* not changed? */
        if (features[i].v_str && features[i].v_str != features[i].def_str)
          MyFree(features[i].v_str); /* free old value */
        features[i].v_str = features[i].def_str;
-       break;
       }
+      break;
     }
-
-    if (features[i].mark) /* call the mark callback if necessary */
-      (*features[i].mark)(features[i].flags & FEAT_MARK ? 1 : 0);
-  }
 }
 
 /* report all F-lines */
@@ -528,29 +541,35 @@ feature_report(struct Client* to)
        (features[i].flags & FEAT_OPER && !IsAnOper(to)))
       continue; /* skip this one */
 
-    if (features[i].report) /* let the callback handle this */
-      (*features[i].report)(to, features[i].flags & FEAT_MARK ? 1 : 0);
-    else if (features[i].flags & FEAT_MARK) { /* it's been changed */
-      switch (features[i].flags & FEAT_MASK) {
-      case FEAT_INT: /* Report an F-line with integer values */
+    switch (features[i].flags & FEAT_MASK) {
+    case FEAT_NONE:
+      if (features[i].report) /* let the callback handle this */
+       (*features[i].report)(to, features[i].flags & FEAT_MARK ? 1 : 0);
+      break;
+
+
+    case FEAT_INT: /* Report an F-line with integer values */
+      if (features[i].flags & FEAT_MARK) /* it's been changed */
        send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F %s %d",
                   features[i].type, features[i].v_int);
-       break;
+      break;
 
-      case FEAT_BOOL: /* Report an F-line with boolean values */
+    case FEAT_BOOL: /* Report an F-line with boolean values */
+      if (features[i].flags & FEAT_MARK) /* it's been changed */
        send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F %s %s",
                   features[i].type, features[i].v_int ? "TRUE" : "FALSE");
-       break;
+      break;
 
-      case FEAT_STR: /* Report an F-line with string values */
+    case FEAT_STR: /* Report an F-line with string values */
+      if (features[i].flags & FEAT_MARK) { /* it's been changed */
        if (features[i].v_str)
          send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F %s %s",
                     features[i].type, features[i].v_str);
        else /* Actually, F:<type> would reset it; you want F:<type>: */
          send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F %s",
                     features[i].type);
-       break;
       }
+      break;
     }
   }
 }