+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.
--- /dev/null
+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>
--- /dev/null
+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>
--- /dev/null
+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>
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,
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,
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,
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, \
#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),
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),
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),
#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 }
};
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;
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;
}
}
(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;
{
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 */
(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;
}
}
}