From 95e583bfb71d54d5ff9b8cff24b5b2ca33073901 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Mon, 18 Dec 2000 21:24:00 +0000 Subject: [PATCH] Author: Kev 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 | 15 ++ doc/api/api.txt | 105 ++++++++++++++ doc/api/features.txt | 203 +++++++++++++++++++++++++++ doc/api/log.txt | 238 +++++++++++++++++++++++++++++++ include/ircd_features.h | 9 +- ircd/ircd_features.c | 303 +++++++++++++++++++++------------------- 6 files changed, 729 insertions(+), 144 deletions(-) create mode 100644 doc/api/api.txt create mode 100644 doc/api/features.txt create mode 100644 doc/api/log.txt diff --git a/ChangeLog b/ChangeLog index e9d58e4..df1ad9a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2000-12-18 Kevin L. Mitchell + + * 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 * 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 index 0000000..5dc9993 --- /dev/null +++ b/doc/api/api.txt @@ -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 " +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 FooBar { + long fb_magic; /* a magic number */ + char *fb_string; /* a string of some sort */ +}; + +The sequence "};" ends the struct declaration. + + + +typedef FooBar_t; /* a simple typedef */ + +This element shows how to hide the inner workings of typedefs. + + + +typedef struct FooBar FooBar_t; /* a more complex typedef */ + +Here we show the full typedef declaration. + + + +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. + + + +#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: + + + +#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. + + + +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. + + +The API document may then end in some summary information, if you +wish, or a ChangeLog of some form, such as follows. + + +Kev + + + +[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 "" section, so we know who to blame ;) + diff --git a/doc/api/features.txt b/doc/api/features.txt new file mode 100644 index 0000000..d388540 --- /dev/null +++ b/doc/api/features.txt @@ -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_() function, as documented +below. + + +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. + + + +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. + + + +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. + + + +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. + + + +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. + + + +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." + + + +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. + + + +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. + + + +int feature_bool(enum Feature feat); + +This function is the complement of feature_int() for features of type +"BOOL." + + + +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. + + + +#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. + + + +#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. + + + +#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). + + + +#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." + + + +Kev + + + +[2000-12-18 Kev] Document the features API + diff --git a/doc/api/log.txt b/doc/api/log.txt new file mode 100644 index 0000000..8f46d52 --- /dev/null +++ b/doc/api/log.txt @@ -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 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 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. + + + +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. + + + +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. + + + +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.) + + + +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(). + + + +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. + + + +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. + + + +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. + + + +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. + + + +char *log_get_file(const char *subsys); + +This returns the current log file name for the given subsystem. + + + +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. + + + +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." + + + +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." + + + +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. + + + +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." + + + +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. + + + +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. + + + +char *log_get_default(void); + +This simply returns ircd's default syslog facility. + + + +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. + + + +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. + + + +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. + + + +Kev + + + +[2000-12-18 Kev] Wrote some documentation on how to use the logging +subsystem. + diff --git a/include/ircd_features.h b/include/ircd_features.h index dd72ea7..758bf5b 100644 --- a/include/ircd_features.h +++ b/include/ircd_features.h @@ -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, diff --git a/ircd/ircd_features.c b/ircd/ircd_features.c index 4dd8d5d..5c23538 100644 --- a/ircd/ircd_features.c +++ b/ircd/ircd_features.c @@ -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: would reset it; you want F:: */ send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F %s", features[i].type); - break; } + break; } } } -- 2.20.1