From: Florian Forster Date: Sat, 11 Apr 2009 07:37:30 +0000 (+0200) Subject: Merge branch 'collectd-4.6' X-Git-Tag: collectd-4.7.0~60 X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=b423bd090e6a1af51446ad6967d5016045bd6ea5;hp=c57f2a191b63ce73fc432befebe9fa3af97c8eb5;p=collectd.git Merge branch 'collectd-4.6' --- diff --git a/README b/README index b6417411..c556c750 100644 --- a/README +++ b/README @@ -76,6 +76,9 @@ Features - filecount Count the number of files in directories. + - gmond + Receive multicast traffic from Ganglia instances. + - hddtemp Harddisk temperatures using hddtempd. @@ -179,6 +182,9 @@ Features - processes Process counts: Number of running, sleeping, zombie, ... processes. + - protocols + Counts various aspects of network protocols such as IP, TCP, UDP, etc. + - rrdcached RRDtool caching daemon (RRDcacheD) statistics. @@ -210,6 +216,9 @@ Features - teamspeak2 TeamSpeak2 server statistics. + - ted + Plugin to read values from `The Energy Detective' (TED). + - thermal Linux ACPI thermal zone information. diff --git a/bindings/java/org/collectd/api/Collectd.java b/bindings/java/org/collectd/api/Collectd.java new file mode 100644 index 00000000..84e65926 --- /dev/null +++ b/bindings/java/org/collectd/api/Collectd.java @@ -0,0 +1,295 @@ +/* + * collectd/java - org/collectd/api/Collectd.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Java API to internal functions of collectd. + * + * All functions in this class are {@code static}. You don't need to create an + * object of this class (in fact, you can't). Just call these functions + * directly. + * + * @author Florian Forster <octo at verplant.org> + */ +public class Collectd +{ + + /** + * Constant for severity (log level) "error". + * + * @see CollectdLogInterface + */ + public static final int LOG_ERR = 3; + + /** + * Constant for severity (log level) "warning". + * + * @see CollectdLogInterface + */ + public static final int LOG_WARNING = 4; + + /** + * Constant for severity (log level) "notice". + * + * @see CollectdLogInterface + */ + public static final int LOG_NOTICE = 5; + + /** + * Constant for severity (log level) "info". + * + * @see CollectdLogInterface + */ + public static final int LOG_INFO = 6; + + /** + * Constant for severity (log level) "debug". + * + * @see CollectdLogInterface + */ + public static final int LOG_DEBUG = 7; + + /** + * Return value of match methods: No match. + * + * This is one of two valid return values from match callbacks, indicating + * that the passed {@link DataSet} and {@link ValueList} did not match. + * + * Do not use the numeric value directly, it is subject to change without + * notice! + * + * @see CollectdMatchInterface + */ + public static final int FC_MATCH_NO_MATCH = 0; + + /** + * Return value of match methods: Match. + * + * This is one of two valid return values from match callbacks, indicating + * that the passed {@link DataSet} and {@link ValueList} did match. + * + * Do not use the numeric value directly, it is subject to change without + * notice! + * + * @see CollectdMatchInterface + */ + public static final int FC_MATCH_MATCHES = 1; + + /** + * Return value of target methods: Continue. + * + * This is one of three valid return values from target callbacks, indicating + * that processing of the {@link ValueList} should continue. + * + * Do not use the numeric value directly, it is subject to change without + * notice! + * + * @see CollectdTargetInterface + */ + public static final int FC_TARGET_CONTINUE = 0; + + /** + * Return value of target methods: Stop. + * + * This is one of three valid return values from target callbacks, indicating + * that processing of the {@link ValueList} should stop immediately. + * + * Do not use the numeric value directly, it is subject to change without + * notice! + * + * @see CollectdTargetInterface + */ + public static final int FC_TARGET_STOP = 1; + + /** + * Return value of target methods: Return. + * + * This is one of three valid return values from target callbacks, indicating + * that processing of the current chain should be stopped and processing of + * the {@link ValueList} should continue in the calling chain. + * + * Do not use the numeric value directly, it is subject to change without + * notice! + * + * @see CollectdTargetInterface + */ + public static final int FC_TARGET_RETURN = 2; + + /** + * Java representation of collectd/src/plugin.h:plugin_register_config + * + * @return Zero when successful, non-zero otherwise. + * @see CollectdConfigInterface + */ + native public static int registerConfig (String name, + CollectdConfigInterface object); + + /** + * Java representation of collectd/src/plugin.h:plugin_register_init + * + * @return Zero when successful, non-zero otherwise. + * @see CollectdInitInterface + */ + native public static int registerInit (String name, + CollectdInitInterface object); + + /** + * Java representation of collectd/src/plugin.h:plugin_register_read + * + * @return Zero when successful, non-zero otherwise. + * @see CollectdReadInterface + */ + native public static int registerRead (String name, + CollectdReadInterface object); + + /** + * Java representation of collectd/src/plugin.h:plugin_register_write + * + * @return Zero when successful, non-zero otherwise. + * @see CollectdWriteInterface + */ + native public static int registerWrite (String name, + CollectdWriteInterface object); + + /** + * Java representation of collectd/src/plugin.h:plugin_register_flush + * + * @return Zero when successful, non-zero otherwise. + * @see CollectdFlushInterface + */ + native public static int registerFlush (String name, + CollectdFlushInterface object); + + /** + * Java representation of collectd/src/plugin.h:plugin_register_shutdown + * + * @return Zero when successful, non-zero otherwise. + * @see CollectdShutdownInterface + */ + native public static int registerShutdown (String name, + CollectdShutdownInterface object); + + /** + * Java representation of collectd/src/plugin.h:plugin_register_log + * + * @return Zero when successful, non-zero otherwise. + * @see CollectdLogInterface + */ + native public static int registerLog (String name, + CollectdLogInterface object); + + /** + * Java representation of collectd/src/plugin.h:plugin_register_notification + * + * @return Zero when successful, non-zero otherwise. + * @see CollectdNotificationInterface + */ + native public static int registerNotification (String name, + CollectdNotificationInterface object); + + /** + * Java representation of collectd/src/filter_chain.h:fc_register_match + * + * @return Zero when successful, non-zero otherwise. + * @see CollectdMatchFactoryInterface + */ + native public static int registerMatch (String name, + CollectdMatchFactoryInterface object); + + /** + * Java representation of collectd/src/filter_chain.h:fc_register_target + * + * @return Zero when successful, non-zero otherwise. + * @see CollectdTargetFactoryInterface + */ + native public static int registerTarget (String name, + CollectdTargetFactoryInterface object); + + /** + * Java representation of collectd/src/plugin.h:plugin_dispatch_values + * + * @return Zero when successful, non-zero otherwise. + */ + native public static int dispatchValues (ValueList vl); + + /** + * Java representation of collectd/src/plugin.h:plugin_dispatch_notification + * + * @return Zero when successful, non-zero otherwise. + */ + native public static int dispatchNotification (Notification n); + + /** + * Java representation of collectd/src/plugin.h:plugin_get_ds + * + * @return The appropriate {@link DataSet} object or {@code null} if no such + * type is registered. + */ + native public static DataSet getDS (String type); + + /** + * Java representation of collectd/src/plugin.h:plugin_log + */ + native private static void log (int severity, String message); + + /** + * Prints an error message. + */ + public static void logError (String message) + { + log (LOG_ERR, message); + } /* void logError */ + + /** + * Prints a warning message. + */ + public static void logWarning (String message) + { + log (LOG_WARNING, message); + } /* void logWarning */ + + /** + * Prints a notice. + */ + public static void logNotice (String message) + { + log (LOG_NOTICE, message); + } /* void logNotice */ + + /** + * Prints an info message. + */ + public static void logInfo (String message) + { + log (LOG_INFO, message); + } /* void logInfo */ + + /** + * Prints a debug message. + */ + public static void logDebug (String message) + { + log (LOG_DEBUG, message); + } /* void logDebug */ +} /* class Collectd */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/bindings/java/org/collectd/api/CollectdConfigInterface.java b/bindings/java/org/collectd/api/CollectdConfigInterface.java new file mode 100644 index 00000000..060f9442 --- /dev/null +++ b/bindings/java/org/collectd/api/CollectdConfigInterface.java @@ -0,0 +1,33 @@ +/* + * collectd/java - org/collectd/api/CollectdConfigInterface.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Interface for objects implementing a config method. + * + * @author Florian Forster <octo at verplant.org> + * @see Collectd#registerConfig(String, CollectdConfigInterface) + */ +public interface CollectdConfigInterface +{ + public int config (OConfigItem ci); +} diff --git a/bindings/java/org/collectd/api/CollectdFlushInterface.java b/bindings/java/org/collectd/api/CollectdFlushInterface.java new file mode 100644 index 00000000..3e492ddf --- /dev/null +++ b/bindings/java/org/collectd/api/CollectdFlushInterface.java @@ -0,0 +1,33 @@ +/* + * collectd/java - org/collectd/api/CollectdFlushInterface.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Interface for objects implementing a flush method. + * + * @author Florian Forster <octo at verplant.org> + * @see Collectd#registerFlush + */ +public interface CollectdFlushInterface +{ + public int flush (int timeout, String identifier); +} diff --git a/bindings/java/org/collectd/api/CollectdInitInterface.java b/bindings/java/org/collectd/api/CollectdInitInterface.java new file mode 100644 index 00000000..fbfd3061 --- /dev/null +++ b/bindings/java/org/collectd/api/CollectdInitInterface.java @@ -0,0 +1,33 @@ +/* + * collectd/java - org/collectd/api/CollectdInitInterface.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Interface for objects implementing an init method. + * + * @author Florian Forster <octo at verplant.org> + * @see Collectd#registerInit + */ +public interface CollectdInitInterface +{ + public int init (); +} diff --git a/bindings/java/org/collectd/api/CollectdLogInterface.java b/bindings/java/org/collectd/api/CollectdLogInterface.java new file mode 100644 index 00000000..ba0350a2 --- /dev/null +++ b/bindings/java/org/collectd/api/CollectdLogInterface.java @@ -0,0 +1,33 @@ +/* + * collectd/java - org/collectd/api/CollectdLogInterface.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Interface for objects implementing a log method. + * + * @author Florian Forster <octo at verplant.org> + * @see Collectd#registerLog + */ +public interface CollectdLogInterface +{ + public void log (int severity, String message); +} diff --git a/bindings/java/org/collectd/api/CollectdMatchFactoryInterface.java b/bindings/java/org/collectd/api/CollectdMatchFactoryInterface.java new file mode 100644 index 00000000..7b1c71a4 --- /dev/null +++ b/bindings/java/org/collectd/api/CollectdMatchFactoryInterface.java @@ -0,0 +1,49 @@ +/* + * collectd/java - org/collectd/api/CollectdMatchFactoryInterface.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Interface for objects implementing a "match factory". + * + * Objects implementing this interface are used to create objects implementing + * the CollectdMatchInterface interface. + * + * @author Florian Forster <octo at verplant.org> + * @see CollectdMatchInterface + * @see Collectd#registerMatch + */ +public interface CollectdMatchFactoryInterface +{ + /** + * Create a new "match" object. + * + * This method uses the configuration provided as argument to create a + * new object which must implement the {@link CollectdMatchInterface} + * interface. + * + * This function corresponds to the create member of the + * src/filter_chain.h:match_proc_t struct. + * + * @return New {@link CollectdMatchInterface} object. + */ + public CollectdMatchInterface createMatch (OConfigItem ci); +} diff --git a/bindings/java/org/collectd/api/CollectdMatchInterface.java b/bindings/java/org/collectd/api/CollectdMatchInterface.java new file mode 100644 index 00000000..cc8a99e6 --- /dev/null +++ b/bindings/java/org/collectd/api/CollectdMatchInterface.java @@ -0,0 +1,48 @@ +/* + * collectd/java - org/collectd/api/CollectdMatchInterface.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Interface for objects implementing a match method. + * + * These objects are instantiated using objects which implement the + * CollectdMatchFactoryInterface interface. They are not instantiated by the + * daemon directly! + * + * @author Florian Forster <octo at verplant.org> + * @see CollectdMatchFactoryInterface + * @see Collectd#registerMatch + */ +public interface CollectdMatchInterface +{ + /** + * Callback method for matches. + * + * This method is called to decide whether or not a given ValueList + * matches or not. How this is determined is the is the main part of + * this function. + * + * @return One of {@link Collectd#FC_MATCH_NO_MATCH} and {@link Collectd#FC_MATCH_MATCHES}. + * @see CollectdMatchFactoryInterface + */ + public int match (DataSet ds, ValueList vl); +} /* public interface CollectdMatchInterface */ diff --git a/bindings/java/org/collectd/api/CollectdNotificationInterface.java b/bindings/java/org/collectd/api/CollectdNotificationInterface.java new file mode 100644 index 00000000..d278fe21 --- /dev/null +++ b/bindings/java/org/collectd/api/CollectdNotificationInterface.java @@ -0,0 +1,33 @@ +/* + * collectd/java - org/collectd/api/CollectdNotificationInterface.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Interface for objects implementing a notification method. + * + * @author Florian Forster <octo at verplant.org> + * @see Collectd#registerNotification + */ +public interface CollectdNotificationInterface +{ + public int notification (Notification n); +} diff --git a/bindings/java/org/collectd/api/CollectdReadInterface.java b/bindings/java/org/collectd/api/CollectdReadInterface.java new file mode 100644 index 00000000..67f1898b --- /dev/null +++ b/bindings/java/org/collectd/api/CollectdReadInterface.java @@ -0,0 +1,47 @@ +/* + * collectd/java - org/collectd/api/CollectdReadInterface.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Interface for objects implementing a read method. + * + * Objects implementing this interface can be registered with the daemon. Their + * read method is then called periodically to acquire and submit values. + * + * @author Florian Forster <octo at verplant.org> + * @see Collectd#registerRead + */ +public interface CollectdReadInterface +{ + /** + * Callback method for read plugins. + * + * This method is called once every few seconds (depends on the + * configuration of the daemon). It is supposed to gather values in + * some way and submit them to the daemon using + * {@link Collectd#dispatchValues}. + * + * @return zero when successful, non-zero when an error occurred. + * @see Collectd#dispatchValues + */ + public int read (); +} diff --git a/bindings/java/org/collectd/api/CollectdShutdownInterface.java b/bindings/java/org/collectd/api/CollectdShutdownInterface.java new file mode 100644 index 00000000..108c54ed --- /dev/null +++ b/bindings/java/org/collectd/api/CollectdShutdownInterface.java @@ -0,0 +1,33 @@ +/* + * collectd/java - org/collectd/api/CollectdShutdownInterface.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Interface for objects implementing a shutdown method. + * + * @author Florian Forster <octo at verplant.org> + * @see Collectd#registerShutdown + */ +public interface CollectdShutdownInterface +{ + public int shutdown (); +} diff --git a/bindings/java/org/collectd/api/CollectdTargetFactoryInterface.java b/bindings/java/org/collectd/api/CollectdTargetFactoryInterface.java new file mode 100644 index 00000000..65f61818 --- /dev/null +++ b/bindings/java/org/collectd/api/CollectdTargetFactoryInterface.java @@ -0,0 +1,49 @@ +/* + * collectd/java - org/collectd/api/CollectdTargetFactoryInterface.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Interface for objects implementing a "target factory". + * + * Objects implementing this interface are used to create objects implementing + * the CollectdTargetInterface interface. + * + * @author Florian Forster <octo at verplant.org> + * @see CollectdTargetInterface + * @see Collectd#registerTarget + */ +public interface CollectdTargetFactoryInterface +{ + /** + * Create a new "target" object. + * + * This method uses the configuration provided as argument to create a + * new object which must implement the {@link CollectdTargetInterface} + * interface. + * + * This function corresponds to the {@code create} member of the + * {@code src/filter_chain.h:target_proc_t} struct. + * + * @return New {@link CollectdTargetInterface} object. + */ + public CollectdTargetInterface createTarget (OConfigItem ci); +} diff --git a/bindings/java/org/collectd/api/CollectdTargetInterface.java b/bindings/java/org/collectd/api/CollectdTargetInterface.java new file mode 100644 index 00000000..74412a32 --- /dev/null +++ b/bindings/java/org/collectd/api/CollectdTargetInterface.java @@ -0,0 +1,48 @@ +/* + * collectd/java - org/collectd/api/CollectdTargetInterface.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Interface for objects implementing a target method. + * + * These objects are instantiated using objects which implement the + * CollectdTargetFactoryInterface interface. They are not instantiated by the + * daemon directly! + * + * @author Florian Forster <octo at verplant.org> + * @see CollectdTargetFactoryInterface + * @see Collectd#registerTarget + */ +public interface CollectdTargetInterface +{ + /** + * Callback method for targets. + * + * This method is called to perform some action on the given ValueList. + * What precisely is done depends entirely on the implementing class. + * + * @return One of: {@link Collectd#FC_TARGET_CONTINUE}, + * {@link Collectd#FC_TARGET_STOP}, {@link Collectd#FC_TARGET_RETURN} + * @see CollectdTargetFactoryInterface + */ + public int invoke (DataSet ds, ValueList vl); +} /* public interface CollectdTargetInterface */ diff --git a/bindings/java/org/collectd/api/CollectdWriteInterface.java b/bindings/java/org/collectd/api/CollectdWriteInterface.java new file mode 100644 index 00000000..28e0230b --- /dev/null +++ b/bindings/java/org/collectd/api/CollectdWriteInterface.java @@ -0,0 +1,33 @@ +/* + * collectd/java - org/collectd/api/CollectdWriteInterface.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Interface for objects implementing a write method. + * + * @author Florian Forster <octo at verplant.org> + * @see Collectd#registerWrite + */ +public interface CollectdWriteInterface +{ + public int write (ValueList vl); +} diff --git a/bindings/java/org/collectd/api/DataSet.java b/bindings/java/org/collectd/api/DataSet.java new file mode 100644 index 00000000..98230730 --- /dev/null +++ b/bindings/java/org/collectd/api/DataSet.java @@ -0,0 +1,137 @@ +/* + * collectd/java - org/collectd/api/OConfigItem.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +import java.util.List; +import java.util.ArrayList; + +/** + * Java representation of collectd/src/plugin.h:data_set_t structure. + * + * @author Florian Forster <octo at verplant.org> + */ +public class DataSet +{ + private String _type; + private List _ds; + + private DataSet () + { + this._type = null; + this._ds = new ArrayList (); + } + + public DataSet (String type) + { + this._type = type; + this._ds = new ArrayList (); + } + + public DataSet (String type, DataSource dsrc) + { + this._type = type; + this._ds = new ArrayList (); + this._ds.add (dsrc); + } + + public DataSet (String type, List ds) + { + this._type = type; + this._ds = ds; + } + + public void setType (String type) + { + this._type = type; + } + + public String getType () + { + return (this._type); + } + + public void addDataSource (DataSource dsrc) + { + this._ds.add (dsrc); + } + + public List getDataSources () + { + return (this._ds); + } + + public String toString () + { + StringBuffer sb = new StringBuffer (); + int i; + + sb.append (this._type); + for (i = 0; i < this._ds.size (); i++) + { + if (i == 0) + sb.append ("\t"); + else + sb.append (", "); + sb.append (this._ds.get (i).toString ()); + } + + return (sb.toString ()); + } + + static public DataSet parseDataSet (String str) + { + DataSet ds = new DataSet (); + String[] fields; + int i; + + str = str.trim(); + if (str.length() == 0) { + return (null); + } + if (str.charAt(0) == '#') { + return (null); + } + + fields = str.split ("\\s+"); + if (fields.length < 2) + return (null); + + ds._type = fields[0]; + + for (i = 1; i < fields.length; i++) { + DataSource dsrc; + + dsrc = DataSource.parseDataSource (fields[i]); + if (dsrc == null) + break; + + ds._ds.add (dsrc); + } + + if (i < fields.length) + return (null); + + return (ds); + } /* DataSet parseDataSet */ +} /* class DataSet */ + +/* vim: set sw=4 sts=4 et : */ diff --git a/bindings/java/org/collectd/api/DataSource.java b/bindings/java/org/collectd/api/DataSource.java new file mode 100644 index 00000000..bfe8e2d0 --- /dev/null +++ b/bindings/java/org/collectd/api/DataSource.java @@ -0,0 +1,145 @@ +/* + * jcollectd + * Copyright (C) 2009 Hyperic, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.collectd.api; + +/** + * Java representation of collectd/src/plugin.h:data_source_t structure. + */ +public class DataSource { + public static final int TYPE_COUNTER = 0; + public static final int TYPE_GAUGE = 1; + + static final String COUNTER = "COUNTER"; + static final String GAUGE = "GAUGE"; + + static final String NAN = "U"; + private static final String[] TYPES = { COUNTER, GAUGE }; + + String _name; + int _type; + double _min; + double _max; + + public DataSource (String name, int type, double min, double max) { + this._name = name; + this._type = TYPE_GAUGE; + if (type == TYPE_COUNTER) + this._type = TYPE_COUNTER; + this._min = min; + this._max = max; + } + + /* Needed in parseDataSource below. Other code should use the above + * constructor or `parseDataSource'. */ + private DataSource () { + this._type = TYPE_GAUGE; + } + + public String getName() { + return _name; + } + + public void setName(String name) { + _name = name; + } + + public int getType() { + return _type; + } + + public void setType(int type) { + _type = type; + } + + public double getMin() { + return _min; + } + + public void setMin(double min) { + _min = min; + } + + public double getMax() { + return _max; + } + + public void setMax(double max) { + _max = max; + } + + static double toDouble(String val) { + if (val.equals(NAN)) { + return Double.NaN; + } + else { + return Double.parseDouble(val); + } + } + + private String asString(double val) { + if (Double.isNaN(val)) { + return NAN; + } + else { + return String.valueOf(val); + } + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + final char DLM = ':'; + sb.append(_name).append(DLM); + sb.append(TYPES[_type]).append(DLM); + sb.append(asString(_min)).append(DLM); + sb.append(asString(_max)); + return sb.toString(); + } + + static public DataSource parseDataSource (String str) + { + String[] fields; + int str_len = str.length (); + DataSource dsrc = new DataSource (); + + /* Ignore trailing commas. This makes it easier for parsing code. */ + if (str.charAt (str_len - 1) == ',') { + str = str.substring (0, str_len - 1); + } + + fields = str.split(":"); + if (fields.length != 4) + return (null); + + dsrc._name = fields[0]; + + if (fields[1].equals (DataSource.GAUGE)) { + dsrc._type = TYPE_GAUGE; + } + else { + dsrc._type = TYPE_COUNTER; + } + + dsrc._min = toDouble (fields[2]); + dsrc._max = toDouble (fields[3]); + + return (dsrc); + } /* DataSource parseDataSource */ +} + +/* vim: set sw=4 sts=4 et : */ diff --git a/bindings/java/org/collectd/api/Notification.java b/bindings/java/org/collectd/api/Notification.java new file mode 100644 index 00000000..cfc21863 --- /dev/null +++ b/bindings/java/org/collectd/api/Notification.java @@ -0,0 +1,88 @@ +/* + * jcollectd + * Copyright (C) 2009 Hyperic, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.collectd.api; + +/** + * Java representation of collectd/src/plugin.h:notfication_t structure. + */ +public class Notification extends PluginData { + public static final int FAILURE = 1; + public static final int WARNING = 2; + public static final int OKAY = 4; + + public static String[] SEVERITY = { + "FAILURE", + "WARNING", + "OKAY", + "UNKNOWN" + }; + + private int _severity; + private String _message; + + public Notification () { + _severity = 0; + _message = "Initial notification message"; + } + + public Notification (PluginData pd) { + super (pd); + _severity = 0; + _message = "Initial notification message"; + } + + public void setSeverity (int severity) { + if ((severity == FAILURE) + || (severity == WARNING) + || (severity == OKAY)) + this._severity = severity; + } + + public int getSeverity() { + return _severity; + } + + public String getSeverityString() { + switch (_severity) { + case FAILURE: + return SEVERITY[0]; + case WARNING: + return SEVERITY[1]; + case OKAY: + return SEVERITY[2]; + default: + return SEVERITY[3]; + } + } + + public void setMessage (String message) { + this._message = message; + } + + public String getMessage() { + return _message; + } + + public String toString() { + StringBuffer sb = new StringBuffer(super.toString()); + sb.append(" [").append(getSeverityString()).append("] "); + sb.append(_message); + return sb.toString(); + } +} diff --git a/bindings/java/org/collectd/api/OConfigItem.java b/bindings/java/org/collectd/api/OConfigItem.java new file mode 100644 index 00000000..4c6a778d --- /dev/null +++ b/bindings/java/org/collectd/api/OConfigItem.java @@ -0,0 +1,91 @@ +/* + * collectd/java - org/collectd/api/OConfigItem.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +import java.util.List; +import java.util.ArrayList; + +/** + * Java representation of collectd/src/liboconfig/oconfig.h:oconfig_item_t structure. + * + * @author Florian Forster <octo at verplant.org> + */ +public class OConfigItem +{ + private String _key = null; + private List _values = new ArrayList (); + private List _children = new ArrayList (); + + public OConfigItem (String key) + { + _key = key; + } /* OConfigItem (String key) */ + + public String getKey () + { + return (_key); + } /* String getKey () */ + + public void addValue (OConfigValue cv) + { + _values.add (cv); + } /* void addValue (OConfigValue cv) */ + + public void addValue (String s) + { + _values.add (new OConfigValue (s)); + } /* void addValue (String s) */ + + public void addValue (Number n) + { + _values.add (new OConfigValue (n)); + } /* void addValue (String s) */ + + public void addValue (boolean b) + { + _values.add (new OConfigValue (b)); + } /* void addValue (String s) */ + + public List getValues () + { + return (_values); + } /* List getValues () */ + + public void addChild (OConfigItem ci) + { + _children.add (ci); + } /* void addChild (OConfigItem ci) */ + + public List getChildren () + { + return (_children); + } /* List getChildren () */ + + public String toString () + { + return (new String ("{ key: " + _key + "; " + + "values: " + _values.toString () + "; " + + "children: " + _children.toString () + "; }")); + } /* String toString () */ +} /* class OConfigItem */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/bindings/java/org/collectd/api/OConfigValue.java b/bindings/java/org/collectd/api/OConfigValue.java new file mode 100644 index 00000000..1ebafff7 --- /dev/null +++ b/bindings/java/org/collectd/api/OConfigValue.java @@ -0,0 +1,96 @@ +/* + * collectd/java - org/collectd/api/OConfigValue.java + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + */ + +package org.collectd.api; + +/** + * Java representation of collectd/src/liboconfig/oconfig.h:oconfig_value_t structure. + * + * @author Florian Forster <octo at verplant.org> + */ +public class OConfigValue +{ + public static final int OCONFIG_TYPE_STRING = 0; + public static final int OCONFIG_TYPE_NUMBER = 1; + public static final int OCONFIG_TYPE_BOOLEAN = 2; + + private int _type; + private String _value_string; + private Number _value_number; + private boolean _value_boolean; + + public OConfigValue (String s) + { + _type = OCONFIG_TYPE_STRING; + _value_string = s; + _value_number = null; + _value_boolean = false; + } /* OConfigValue (String s) */ + + public OConfigValue (Number n) + { + _type = OCONFIG_TYPE_NUMBER; + _value_string = null; + _value_number = n; + _value_boolean = false; + } /* OConfigValue (String s) */ + + public OConfigValue (boolean b) + { + _type = OCONFIG_TYPE_BOOLEAN; + _value_string = null; + _value_number = null; + _value_boolean = b; + } /* OConfigValue (String s) */ + + public int getType () + { + return (_type); + } /* int getType */ + + public String getString () + { + return (_value_string); + } /* String getString */ + + public Number getNumber () + { + return (_value_number); + } /* String getString */ + + public boolean getBoolean () + { + return (_value_boolean); + } /* String getString */ + + public String toString () + { + if (_type == OCONFIG_TYPE_STRING) + return (_value_string); + else if (_type == OCONFIG_TYPE_NUMBER) + return (_value_number.toString ()); + else if (_type == OCONFIG_TYPE_BOOLEAN) + return (Boolean.toString (_value_boolean)); + return (null); + } /* String toString () */ +} /* class OConfigValue */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/bindings/java/org/collectd/api/PluginData.java b/bindings/java/org/collectd/api/PluginData.java new file mode 100644 index 00000000..26b0206d --- /dev/null +++ b/bindings/java/org/collectd/api/PluginData.java @@ -0,0 +1,127 @@ +/* + * jcollectd + * Copyright (C) 2009 Hyperic, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.collectd.api; + +import java.util.Date; + +/** + * Shared members of value_list_t and notification_t structures. + */ +public class PluginData { + + protected long _time = 0; + protected String _host; + protected String _plugin; + protected String _pluginInstance = ""; + protected String _type = ""; + protected String _typeInstance = ""; + + public PluginData() { + + } + + public PluginData(PluginData pd) { + _time = pd._time; + _host = pd._host; + _plugin = pd._plugin; + _pluginInstance = pd._pluginInstance; + _type = pd._type; + _typeInstance = pd._typeInstance; + } + + public long getTime() { + return _time; + } + + public void setTime(long time) { + _time = time; + } + + public String getHost() { + return _host; + } + + public void setHost(String host) { + _host = host; + } + + public String getPlugin() { + return _plugin; + } + + public void setPlugin(String plugin) { + _plugin = plugin; + } + + public String getPluginInstance() { + return _pluginInstance; + } + + public void setPluginInstance(String pluginInstance) { + _pluginInstance = pluginInstance; + } + + public String getType() { + return _type; + } + + public void setType(String type) { + _type = type; + } + + public String getTypeInstance() { + return _typeInstance; + } + + public void setTypeInstance(String typeInstance) { + _typeInstance = typeInstance; + } + + public boolean defined(String val) { + return (val != null) && (val.length() > 0); + } + + public String getSource() { + final char DLM = '/'; + StringBuffer sb = new StringBuffer(); + if (defined(_host)) { + sb.append(_host); + } + if (defined(_plugin)) { + sb.append(DLM).append(_plugin); + } + if (defined(_pluginInstance)) { + sb.append(DLM).append(_pluginInstance); + } + if (defined(_type)) { + sb.append(DLM).append(_type); + } + if (defined(_typeInstance)) { + sb.append(DLM).append(_typeInstance); + } + return sb.toString(); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append('[').append(new Date(_time)).append("] "); + sb.append(getSource()); + return sb.toString(); + } +} diff --git a/bindings/java/org/collectd/api/ValueList.java b/bindings/java/org/collectd/api/ValueList.java new file mode 100644 index 00000000..1baeff24 --- /dev/null +++ b/bindings/java/org/collectd/api/ValueList.java @@ -0,0 +1,122 @@ +/* + * jcollectd + * Copyright (C) 2009 Hyperic, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.collectd.api; + +import java.util.ArrayList; +import java.util.List; + +/** + * Java representation of collectd/src/plugin.h:value_list_t structure. + */ +public class ValueList extends PluginData { + + private List _values = new ArrayList(); + private DataSet _ds; + + private long _interval = 0; + + public ValueList() { + + } + + public ValueList(PluginData pd) { + super(pd); + } + + public ValueList(ValueList vl) { + this((PluginData)vl); + _interval = vl._interval; + _values.addAll(vl.getValues()); + _ds = vl._ds; + } + + public List getValues() { + return _values; + } + + public void setValues(List values) { + _values = values; + } + + public void addValue(Number value) { + _values.add(value); + } + + /* Used by the network parsing code */ + public void clearValues () { + _values.clear (); + } + + /** + * @deprecated Use {@link #getDataSet()} instead. + */ + public List getDataSource() { + if (_ds == null) + return null; + return _ds.getDataSources (); + } + + public DataSet getDataSet () { + return _ds; + } + + public void setDataSet (DataSet ds) { + _ds = ds; + } + + /** + * @deprecated Use {@link #setDataSet(DataSet)} instead. + */ + public void setDataSource(List dsrc) { + _ds = new DataSet (_type, dsrc); + } + + public long getInterval() { + return _interval; + } + + public void setInterval(long interval) { + _interval = interval; + } + + public String toString() { + StringBuffer sb = new StringBuffer(super.toString()); + sb.append("=["); + List ds = getDataSource(); + int size = _values.size(); + for (int i=0; i + */ + +package org.collectd.java; + +import java.util.List; +import java.util.Date; + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryUsage; +import java.lang.management.MemoryMXBean; + +import javax.management.MBeanServerConnection; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +import org.collectd.api.Collectd; +import org.collectd.api.DataSet; +import org.collectd.api.ValueList; +import org.collectd.api.Notification; +import org.collectd.api.OConfigItem; + +import org.collectd.api.CollectdConfigInterface; +import org.collectd.api.CollectdInitInterface; +import org.collectd.api.CollectdReadInterface; +import org.collectd.api.CollectdShutdownInterface; + +import org.collectd.api.OConfigValue; +import org.collectd.api.OConfigItem; + +public class JMXMemory implements CollectdConfigInterface, /* {{{ */ + CollectdInitInterface, + CollectdReadInterface, + CollectdShutdownInterface +{ + private String _jmx_service_url = null; + private MemoryMXBean _mbean = null; + + public JMXMemory () + { + Collectd.registerConfig ("JMXMemory", this); + Collectd.registerInit ("JMXMemory", this); + Collectd.registerRead ("JMXMemory", this); + Collectd.registerShutdown ("JMXMemory", this); + } + + private void submit (String plugin_instance, MemoryUsage usage) /* {{{ */ + { + ValueList vl; + + long mem_init; + long mem_used; + long mem_committed; + long mem_max; + + mem_init = usage.getInit (); + mem_used = usage.getUsed (); + mem_committed = usage.getCommitted (); + mem_max = usage.getMax (); + + Collectd.logDebug ("JMXMemory plugin: plugin_instance = " + plugin_instance + "; " + + "mem_init = " + mem_init + "; " + + "mem_used = " + mem_used + "; " + + "mem_committed = " + mem_committed + "; " + + "mem_max = " + mem_max + ";"); + + vl = new ValueList (); + + vl.setHost ("localhost"); + vl.setPlugin ("JMXMemory"); + vl.setPluginInstance (plugin_instance); + vl.setType ("memory"); + + if (mem_init >= 0) + { + vl.addValue (mem_init); + vl.setTypeInstance ("init"); + Collectd.dispatchValues (vl); + vl.clearValues (); + } + + if (mem_used >= 0) + { + vl.addValue (mem_used); + vl.setTypeInstance ("used"); + Collectd.dispatchValues (vl); + vl.clearValues (); + } + + if (mem_committed >= 0) + { + vl.addValue (mem_committed); + vl.setTypeInstance ("committed"); + Collectd.dispatchValues (vl); + vl.clearValues (); + } + + if (mem_max >= 0) + { + vl.addValue (mem_max); + vl.setTypeInstance ("max"); + Collectd.dispatchValues (vl); + vl.clearValues (); + } + } /* }}} void submit */ + + private int configServiceURL (OConfigItem ci) /* {{{ */ + { + List values; + OConfigValue cv; + + values = ci.getValues (); + if (values.size () != 1) + { + Collectd.logError ("JMXMemory plugin: The JMXServiceURL option needs " + + "exactly one string argument."); + return (-1); + } + + cv = values.get (0); + if (cv.getType () != OConfigValue.OCONFIG_TYPE_STRING) + { + Collectd.logError ("JMXMemory plugin: The JMXServiceURL option needs " + + "exactly one string argument."); + return (-1); + } + + _jmx_service_url = cv.getString (); + return (0); + } /* }}} int configServiceURL */ + + public int config (OConfigItem ci) /* {{{ */ + { + List children; + int i; + + Collectd.logDebug ("JMXMemory plugin: config: ci = " + ci + ";"); + + children = ci.getChildren (); + for (i = 0; i < children.size (); i++) + { + OConfigItem child; + String key; + + child = children.get (i); + key = child.getKey (); + if (key.equalsIgnoreCase ("JMXServiceURL")) + { + configServiceURL (child); + } + else + { + Collectd.logError ("JMXMemory plugin: Unknown config option: " + key); + } + } + + return (0); + } /* }}} int config */ + + public int init () /* {{{ */ + { + JMXServiceURL service_url; + JMXConnector connector; + MBeanServerConnection connection; + + if (_jmx_service_url == null) + { + Collectd.logError ("JMXMemory: _jmx_service_url == null"); + return (-1); + } + + try + { + service_url = new JMXServiceURL (_jmx_service_url); + connector = JMXConnectorFactory.connect (service_url); + connection = connector.getMBeanServerConnection (); + _mbean = ManagementFactory.newPlatformMXBeanProxy (connection, + ManagementFactory.MEMORY_MXBEAN_NAME, + MemoryMXBean.class); + } + catch (Exception e) + { + Collectd.logError ("JMXMemory: Creating MBean failed: " + e); + return (-1); + } + + return (0); + } /* }}} int init */ + + public int read () /* {{{ */ + { + if (_mbean == null) + { + Collectd.logError ("JMXMemory: _mbean == null"); + return (-1); + } + + submit ("heap", _mbean.getHeapMemoryUsage ()); + submit ("non_heap", _mbean.getNonHeapMemoryUsage ()); + + return (0); + } /* }}} int read */ + + public int shutdown () /* {{{ */ + { + System.out.print ("org.collectd.java.JMXMemory.Shutdown ();\n"); + _jmx_service_url = null; + _mbean = null; + return (0); + } /* }}} int shutdown */ +} /* }}} class JMXMemory */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/clean.sh b/clean.sh index 5733f2ea..098669da 100755 --- a/clean.sh +++ b/clean.sh @@ -43,4 +43,8 @@ true \ && rm -f src/libping/config.h.in \ && rm -f src/libping/Makefile \ && rm -f src/libping/Makefile.in \ -&& rm -f src/libping/stamp-h2 +&& rm -f src/libping/stamp-h2 \ +&& rm -f -r src/libcollectdclient/.libs \ +&& rm -f src/libcollectdclient/*.o \ +&& rm -f src/libcollectdclient/*.la \ +&& rm -f src/libcollectdclient/*.lo diff --git a/configure.in b/configure.in index 2344cdb5..f0c43121 100644 --- a/configure.in +++ b/configure.in @@ -1187,6 +1187,166 @@ AC_DEFINE_UNQUOTED(COLLECT_LIBESMTP, [$collect_libesmtp], AM_CONDITIONAL(BUILD_WITH_LIBESMTP, test "x$with_libesmtp" = "xyes") # }}} +# --with-libganglia {{{ +AC_ARG_WITH(libganglia, [AS_HELP_STRING([--with-libganglia@<:@=PREFIX@:>@], [Path to libganglia.])], +[ + if test -f "$withval" && test -x "$withval" + then + with_libganglia_config="$withval" + with_libganglia="yes" + else if test -f "$withval/bin/ganglia-config" && test -x "$withval/bin/ganglia-config" + then + with_libganglia_config="$withval/bin/ganglia-config" + with_libganglia="yes" + else if test -d "$withval" + then + GANGLIA_CPPFLAGS="-I$withval/include" + GANGLIA_LDFLAGS="-L$withval/lib" + with_libganglia="yes" + else + with_libganglia_config="ganglia-config" + with_libganglia="$withval" + fi; fi; fi +], +[ + with_libganglia_config="ganglia-config" + with_libganglia="yes" +]) + +if test "x$with_libganglia" = "xyes" && test "x$with_libganglia_config" != "x" +then + if test "x$GANGLIA_CPPFLAGS" = "x" + then + GANGLIA_CPPFLAGS=`"$with_libganglia_config" --cflags 2>/dev/null` + fi + + if test "x$GANGLIA_LDFLAGS" = "x" + then + GANGLIA_LDFLAGS=`"$with_libganglia_config" --ldflags 2>/dev/null` + fi + + if test "x$GANGLIA_LIBS" = "x" + then + GANGLIA_LIBS=`"$with_libganglia_config" --libs 2>/dev/null` + fi +fi + +SAVE_CPPFLAGS="$CPPFLAGS" +SAVE_LDFLAGS="$LDFLAGS" +CPPFLAGS="$CPPFLAGS $GANGLIA_CPPFLAGS" +LDFLAGS="$LDFLAGS $GANGLIA_LDFLAGS" + +if test "x$with_libganglia" = "xyes" +then + AC_CHECK_HEADERS(gm_protocol.h, + [ + AC_DEFINE(HAVE_GM_PROTOCOL_H, 1, + [Define to 1 if you have the header file.]) + ], [with_libganglia="no (gm_protocol.h not found)"]) +fi + +if test "x$with_libganglia" = "xyes" +then + AC_CHECK_LIB(ganglia, xdr_Ganglia_value_msg, + [ + AC_DEFINE(HAVE_LIBGANGLIA, 1, + [Define to 1 if you have the ganglia library (-lganglia).]) + ], [with_libganglia="no (symbol xdr_Ganglia_value_msg not found)"]) +fi + +CPPFLAGS="$SAVE_CPPFLAGS" +LDFLAGS="$SAVE_LDFLAGS" + +AC_SUBST(GANGLIA_CPPFLAGS) +AC_SUBST(GANGLIA_LDFLAGS) +AC_SUBST(GANGLIA_LIBS) +AM_CONDITIONAL(BUILD_WITH_LIBGANGLIA, test "x$with_libganglia" = "xyes") +# }}} + +# --with-libgcrypt {{{ +GCRYPT_CPPFLAGS="$GCRYPT_CPPFLAGS" +GCRYPT_LDFLAGS="$GCRYPT_LDFLAGS" +GCRYPT_LIBS="$GCRYPT_LIBS" +AC_ARG_WITH(libgcrypt, [AS_HELP_STRING([--with-libgcrypt@<:@=PREFIX@:>@], [Path to libgcrypt.])], +[ + if test -f "$withval" && test -x "$withval" + then + with_libgcrypt_config="$withval" + with_libgcrypt="yes" + else if test -f "$withval/bin/gcrypt-config" && test -x "$withval/bin/gcrypt-config" + then + with_libgcrypt_config="$withval/bin/gcrypt-config" + with_libgcrypt="yes" + else if test -d "$withval" + then + GCRYPT_CPPFLAGS="$GCRYPT_CPPFLAGS -I$withval/include" + GCRYPT_LDFLAGS="$GCRYPT_LDFLAGS -L$withval/lib" + with_libgcrypt="yes" + else + with_libgcrypt_config="gcrypt-config" + with_libgcrypt="$withval" + fi; fi; fi +], +[ + with_libgcrypt_config="libgcrypt-config" + with_libgcrypt="yes" +]) + +if test "x$with_libgcrypt" = "xyes" && test "x$with_libgcrypt_config" != "x" +then + if test "x$GCRYPT_CPPFLAGS" = "x" + then + GCRYPT_CPPFLAGS=`"$with_libgcrypt_config" --cflags 2>/dev/null` + fi + + if test "x$GCRYPT_LDFLAGS" = "x" + then + gcrypt_exec_prefix=`"$with_libgcrypt_config" --exec-prefix 2>/dev/null` + GCRYPT_LDFLAGS="-L$gcrypt_exec_prefix/lib" + fi + + if test "x$GCRYPT_LIBS" = "x" + then + GCRYPT_LIBS=`"$with_libgcrypt_config" --libs 2>/dev/null` + fi +fi + +SAVE_CPPFLAGS="$CPPFLAGS" +SAVE_LDFLAGS="$LDFLAGS" +CPPFLAGS="$CPPFLAGS $GCRYPT_CPPFLAGS" +LDFLAGS="$LDFLAGS $GCRYPT_LDFLAGS" + +if test "x$with_libgcrypt" = "xyes" +then + if test "x$GCRYPT_CPPFLAGS" != "x" + then + AC_MSG_NOTICE([gcrypt CPPFLAGS: $GCRYPT_CPPFLAGS]) + fi + AC_CHECK_HEADERS(gcrypt.h, + [with_libgcrypt="yes"], + [with_libgcrypt="no (gcrypt.h not found)"]) +fi + +if test "x$with_libgcrypt" = "xyes" +then + if test "x$GCRYPT_LDFLAGS" != "x" + then + AC_MSG_NOTICE([gcrypt LDFLAGS: $GCRYPT_LDFLAGS]) + fi + AC_CHECK_LIB(gcrypt, gcry_md_hash_buffer, + [with_libgcrypt="yes"], + [with_libgcrypt="no (symbol gcry_md_hash_buffer not found)"]) +fi + +CPPFLAGS="$SAVE_CPPFLAGS" +LDFLAGS="$SAVE_LDFLAGS" + +AC_SUBST(GCRYPT_CPPFLAGS) +AC_SUBST(GCRYPT_LDFLAGS) +AC_SUBST(GCRYPT_LIBS) +AM_CONDITIONAL(BUILD_WITH_LIBGCRYPT, test "x$with_libgcrypt" = "xyes") +# }}} + # --with-libiptc {{{ with_own_libiptc="no" AC_ARG_WITH(libiptc, [AS_HELP_STRING([--with-libiptc@<:@=PREFIX@:>@], [Path to libiptc.])], @@ -1254,6 +1414,147 @@ then fi # }}} +# --with-java {{{ +with_java_home="$JAVA_HOME" +with_java_vmtype="client" +with_java_cflags="" +with_java_libs="" +AC_ARG_WITH(java, [AS_HELP_STRING([--with-java@<:@=PREFIX@:>@], [Path to Java home.])], +[ + if test "x$withval" = "xno" + then + with_java="no" + else if test "x$withval" = "xyes" + then + with_java="yes" + else + with_java_home="$withval" + with_java="yes" + fi; fi +], +[with_java="yes"]) +if test "x$with_java" = "xyes" +then + if test -d "$with_java_home" + then + if test -d "$with_java_home/include" + then + JAVA_CPPFLAGS="$JAVA_CPPFLAGS -I$with_java_home/include" + else + JAVA_CPPFLAGS="$JAVA_CPPFLAGS -I$with_java_home" + fi + + if test -d "$with_java_home/lib" + then + JAVA_LDFLAGS="$JAVA_LDFLAGS -L$with_java_home/lib" + else + JAVA_LDFLAGS="$JAVA_LDFLAGS -L$with_java_home" + fi + else if test "x$with_java_home" != "x" + then + AC_MSG_WARN([JAVA_HOME: No such directory: $with_java_home]) + fi; fi +fi + +if test "x$JAVA_CPPFLAGS" != "x" +then + AC_MSG_NOTICE([Building with JAVA_CPPFLAGS set to: $JAVA_CPPFLAGS]) +fi +if test "x$JAVA_CFLAGS" != "x" +then + AC_MSG_NOTICE([Building with JAVA_CFLAGS set to: $JAVA_CFLAGS]) +fi +if test "x$JAVA_LDFLAGS" != "x" +then + AC_MSG_NOTICE([Building with JAVA_LDFLAGS set to: $JAVA_LDFLAGS]) +fi + +SAVE_CPPFLAGS="$CPPFLAGS" +SAVE_CFLAGS="$CFLAGS" +SAVE_LDFLAGS="$LDFLAGS" +CPPFLAGS="$CPPFLAGS $JAVA_CPPFLAGS" +CFLAGS="$CFLAGS $JAVA_CFLAGS" +LDFLAGS="$LDFLAGS $JAVA_LDFLAGS" + +if test "x$with_java" = "xyes" +then + AC_CHECK_HEADERS(jni.h, [], [with_java="no (jni.h not found)"]) +fi +if test "x$with_java" = "xyes" +then + AC_CHECK_LIB(jvm, JNI_CreateJavaVM, + [with_java="yes"], + [with_java="no (libjvm not found)"], + [$JAVA_LIBS]) +fi +if test "x$with_java" = "xyes" +then + JAVA_LIBS="$JAVA_LIBS -ljvm" + AC_MSG_NOTICE([Building with JAVA_LIBS set to: $JAVA_LIBS]) +fi + +CPPFLAGS="$SAVE_CPPFLAGS" +CFLAGS="$SAVE_CFLAGS" +LDFLAGS="$SAVE_LDFLAGS" + +AC_SUBST(JAVA_CPPFLAGS) +AC_SUBST(JAVA_CFLAGS) +AC_SUBST(JAVA_LDFLAGS) +AC_SUBST(JAVA_LIBS) +AM_CONDITIONAL(BUILD_WITH_JAVA, test "x$with_java" = "xyes") +# }}} + +# --with-libmemcached {{{ +with_libmemcached_cppflags="" +with_libmemcached_ldflags="" +AC_ARG_WITH(libmemcached, [AS_HELP_STRING([--with-libmemcached@<:@=PREFIX@:>@], [Path to libmemcached.])], +[ + if test "x$withval" != "xno" && test "x$withval" != "xyes" + then + with_libmemcached_cppflags="-I$withval/include" + with_libmemcached_ldflags="-L$withval/lib" + with_libmemcached="yes" + else + with_libmemcached="$withval" + fi +], +[ + with_libmemcached="yes" +]) +if test "x$with_libmemcached" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_libmemcached_cppflags" + + AC_CHECK_HEADERS(libmemcached/memcached.h, [with_libmemcached="yes"], [with_libmemcached="no (libmemcached/memcached.h not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" +fi +if test "x$with_libmemcached" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + SAVE_LDFLAGS="$LDFLAGS" + CPPFLAGS="$CPPFLAGS $with_libmemcached_cppflags" + LDFLAGS="$LDFLAGS $with_libmemcached_ldflags" + + AC_CHECK_LIB(memcached, memcached_create, [with_libmemcached="yes"], [with_libmemcached="no (Symbol 'memcached_create' not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" +fi +if test "x$with_libmemcached" = "xyes" +then + BUILD_WITH_LIBMEMCACHED_CPPFLAGS="$with_libmemcached_cppflags" + BUILD_WITH_LIBMEMCACHED_LDFLAGS="$with_libmemcached_ldflags" + BUILD_WITH_LIBMEMCACHED_LIBS="-lmemcached" + AC_SUBST(BUILD_WITH_LIBMEMCACHED_CPPFLAGS) + AC_SUBST(BUILD_WITH_LIBMEMCACHED_LDFLAGS) + AC_SUBST(BUILD_WITH_LIBMEMCACHED_LIBS) + AC_DEFINE(HAVE_LIBMEMCACHED, 1, [Define if libmemcached is present and usable.]) +fi +AM_CONDITIONAL(BUILD_WITH_LIBMEMCACHED, test "x$with_libmemcached" = "xyes") +# }}} + # --with-libmysql {{{ with_mysql_config="mysql_config" with_mysql_cflags="" @@ -1556,55 +1857,60 @@ fi # }}} # --with-liboping {{{ -with_own_liboping="no" -liboping_LDFLAGS="$LDFLAGS" -liboping_CPPFLAGS="$CPPFLAGS" AC_ARG_WITH(liboping, [AS_HELP_STRING([--with-liboping@<:@=PREFIX@:>@], [Path to liboping.])], [ - if test "x$withval" != "xno" && test "x$withval" != "xyes" + if test "x$withval" = "xyes" + then + with_liboping="yes" + else if test "x$withval" = "xno" + then + with_liboping="no" + else + with_liboping="yes" + LIBOPING_CPPFLAGS="$LIBOPING_CPPFLAGS -I$withval/include" + LIBOPING_LDFLAGS="$LIBOPING_LDFLAGS -L$withval/lib" + fi; fi +], +[with_liboping="yes"]) + +SAVE_CPPFLAGS="$CPPFLAGS" +SAVE_LDFLAGS="$LDFLAGS" + +CPPFLAGS="$CPPFLAGS $LIBOPING_CPPFLAGS" +LDFLAGS="$LDFLAGS $LIBOPING_LDFLAGS" + +if test "x$with_liboping" = "xyes" +then + if test "x$LIBOPING_CPPFLAGS" != "x" then - if test -d "$withval/lib" - then - liboping_LDFLAGS="$LDFLAGS -L$withval/lib" - fi - if test -d "$withval/include" - then - liboping_CPPFLAGS="$CPPFLAGS -I$withval/include" - fi + AC_MSG_NOTICE([liboping CPPFLAGS: $LIBOPING_CPPFLAGS]) fi - if test "x$withval" = "xno" - then - with_liboping="no" - with_own_liboping="no" - else if test "x$withval" = "xyes" + AC_CHECK_HEADERS(oping.h, + [with_liboping="yes"], + [with_liboping="no ('oping.h' not found)"]) +fi +if test "x$with_liboping" = "xyes" +then + if test "x$LIBOPING_LDFLAGS" != "x" then - with_liboping="yes" - fi; fi -], -[ - with_liboping="yes" -]) + AC_MSG_NOTICE([liboping LDFLAGS: $LIBOPING_LDFLAGS]) + fi + AC_CHECK_LIB(oping, ping_construct, + [with_liboping="yes"], + [with_liboping="no (symbol 'ping_construct' not found)"]) +fi + +CPPFLAGS="$SAVE_CPPFLAGS" +LDFLAGS="$SAVE_LDFLAGS" if test "x$with_liboping" = "xyes" then - save_LDFLAGS="$LDFLAGS" - save_CPPFLAGS="$CPPFLAGS" - LDFLAGS="$liboping_LDFLAGS" - CPPFLAGS="$liboping_CPPFLAGS" - AC_CHECK_LIB(oping, ping_construct, - [ - with_liboping="yes" - with_own_liboping="no" - ], - [ - with_liboping="yes" - with_own_liboping="yes" - LDFLAGS="$save_LDFLAGS" - CPPFLAGS="$save_CPPFLAGS" - ]) + BUILD_WITH_LIBOPING_CPPFLAGS="$LIBOPING_CPPFLAGS" + BUILD_WITH_LIBOPING_LDFLAGS="$LIBOPING_LDFLAGS" + AC_SUBST(BUILD_WITH_LIBOPING_CPPFLAGS) + AC_SUBST(BUILD_WITH_LIBOPING_LDFLAGS) fi AM_CONDITIONAL(BUILD_WITH_LIBOPING, test "x$with_liboping" = "xyes") -AM_CONDITIONAL(BUILD_WITH_OWN_LIBOPING, test "x$with_own_liboping" = "xyes") # }}} # --with-oracle {{{ @@ -2022,6 +2328,10 @@ then [with_libpq="yes"], [with_libpq="no (symbol 'PQconnectdb' not found)"]) + AC_CHECK_LIB(pq, PQserverVersion, + [with_libpq="yes"], + [with_libpq="no (symbol 'PQserverVersion' not found)"]) + LDFLAGS="$SAVE_LDFLAGS" fi if test "x$with_libpq" = "xyes" @@ -2858,12 +3168,15 @@ plugin_multimeter="no" plugin_nfs="no" plugin_perl="no" plugin_processes="no" +plugin_protocols="no" plugin_serial="no" plugin_swap="no" plugin_tape="no" plugin_tcpconns="no" +plugin_ted="no" plugin_thermal="no" plugin_users="no" +plugin_uptime="no" plugin_vmem="no" plugin_vserver="no" plugin_wireless="no" @@ -2882,10 +3195,12 @@ then plugin_memory="yes" plugin_nfs="yes" plugin_processes="yes" + plugin_protocols="yes" plugin_serial="yes" plugin_swap="yes" plugin_tcpconns="yes" plugin_thermal="yes" + plugin_uptime="yes" plugin_vmem="yes" plugin_vserver="yes" plugin_wireless="yes" @@ -2909,6 +3224,11 @@ then fi # Solaris +if test "x$with_kstat" = "xyes" +then + plugin_uptime="yes" +fi + if test "x$with_devinfo$with_kstat" = "xyesyes" then plugin_cpu="yes" @@ -2954,6 +3274,7 @@ if test "x$have_sysctl" = "xyes" then plugin_cpu="yes" plugin_swap="yes" + plugin_uptime="yes" fi if test "x$have_sysctlbyname" = "xyes" then @@ -3000,6 +3321,7 @@ fi if test "x$have_termios_h" = "xyes" then plugin_multimeter="yes" + plugin_ted="yes" fi if test "x$have_thread_info" = "xyes" @@ -3057,12 +3379,14 @@ AC_PLUGIN([email], [yes], [EMail statistics]) AC_PLUGIN([entropy], [$plugin_entropy], [Entropy statistics]) AC_PLUGIN([exec], [yes], [Execution of external programs]) AC_PLUGIN([filecount], [yes], [Count files in directories]) +AC_PLUGIN([gmond], [$with_libganglia], [Ganglia plugin]) AC_PLUGIN([hddtemp], [yes], [Query hddtempd]) AC_PLUGIN([interface], [$plugin_interface], [Interface traffic statistics]) AC_PLUGIN([ipmi], [$plugin_ipmi], [IPMI sensor statistics]) AC_PLUGIN([iptables], [$with_libiptc], [IPTables rule counters]) AC_PLUGIN([ipvs], [$plugin_ipvs], [IPVS connection statistics]) AC_PLUGIN([irq], [$plugin_irq], [IRQ statistics]) +AC_PLUGIN([java], [$with_java], [Embed the Java Virtual Machine]) AC_PLUGIN([libvirt], [$plugin_libvirt], [Virtual machine statistics]) AC_PLUGIN([load], [$plugin_load], [System load]) AC_PLUGIN([logfile], [yes], [File logging plugin]) @@ -3070,6 +3394,7 @@ AC_PLUGIN([match_regex], [yes], [The regex match]) AC_PLUGIN([match_timediff], [yes], [The timediff match]) AC_PLUGIN([match_value], [yes], [The value match]) AC_PLUGIN([mbmon], [yes], [Query mbmond]) +AC_PLUGIN([memcachec], [$with_libmemcached], [memcachec statistics]) AC_PLUGIN([memcached], [yes], [memcached statistics]) AC_PLUGIN([memory], [$plugin_memory], [Memory usage]) AC_PLUGIN([multimeter], [$plugin_multimeter], [Read multimeter values]) @@ -3090,6 +3415,7 @@ AC_PLUGIN([ping], [$with_liboping], [Network latency statistics]) AC_PLUGIN([postgresql], [$with_libpq], [PostgreSQL database statistics]) AC_PLUGIN([powerdns], [yes], [PowerDNS statistics]) AC_PLUGIN([processes], [$plugin_processes], [Process statistics]) +AC_PLUGIN([protocols], [$plugin_protocols], [Protocol (IP, TCP, ...) statistics]) AC_PLUGIN([rrdcached], [$librrd_rrdc_update], [RRDTool output plugin]) AC_PLUGIN([rrdtool], [$with_librrd], [RRDTool output plugin]) AC_PLUGIN([sensors], [$with_libsensors], [lm_sensors statistics]) @@ -3097,6 +3423,7 @@ AC_PLUGIN([serial], [$plugin_serial], [serial port traffic]) AC_PLUGIN([snmp], [$with_libnetsnmp], [SNMP querying plugin]) AC_PLUGIN([swap], [$plugin_swap], [Swap usage statistics]) AC_PLUGIN([syslog], [$have_syslog], [Syslog logging plugin]) +AC_PLUGIN([table], [yes], [Parsing of tabular data]) AC_PLUGIN([tail], [yes], [Parsing of logfiles]) AC_PLUGIN([tape], [$plugin_tape], [Tape drive statistics]) AC_PLUGIN([target_notification], [yes], [The notification target]) @@ -3104,8 +3431,10 @@ AC_PLUGIN([target_replace], [yes], [The replace target]) AC_PLUGIN([target_set], [yes], [The set target]) AC_PLUGIN([tcpconns], [$plugin_tcpconns], [TCP connection statistics]) AC_PLUGIN([teamspeak2], [yes], [TeamSpeak2 server statistics]) +AC_PLUGIN([ted], [$plugin_ted], [Read The Energy Detective values]) AC_PLUGIN([thermal], [$plugin_thermal], [Linux ACPI thermal zone statistics]) AC_PLUGIN([unixsock], [yes], [Unixsock communication plugin]) +AC_PLUGIN([uptime], [$plugin_uptime], [Uptime statistics]) AC_PLUGIN([users], [$plugin_users], [User statistics]) AC_PLUGIN([uuid], [yes], [UUID as hostname plugin]) AC_PLUGIN([vmem], [$plugin_vmem], [Virtual memory statistics]) @@ -3113,6 +3442,86 @@ AC_PLUGIN([vserver], [$plugin_vserver], [Linux VServer statistics]) AC_PLUGIN([wireless], [$plugin_wireless], [Wireless statistics]) AC_PLUGIN([xmms], [$with_libxmms], [XMMS statistics]) +dnl Default configuration file +# Load either syslog or logfile +LOAD_PLUGIN_SYSLOG="" +LOAD_PLUGIN_LOGFILE="" + +AC_MSG_CHECKING([which default log plugin to load]) +default_log_plugin="none" +if test "x$enable_syslog" = "xyes" +then + default_log_plugin="syslog" +else + LOAD_PLUGIN_SYSLOG="##" +fi + +if test "x$enable_logfile" = "xyes" +then + if test "x$default_log_plugin" = "xnone" + then + default_log_plugin="logfile" + else + LOAD_PLUGIN_LOGFILE="#" + fi +else + LOAD_PLUGIN_LOGFILE="##" +fi +AC_MSG_RESULT([$default_log_plugin]) + +AC_SUBST(LOAD_PLUGIN_SYSLOG) +AC_SUBST(LOAD_PLUGIN_LOGFILE) + +DEFAULT_LOG_LEVEL="info" +if test "x$enable_debug" = "xyes" +then + DEFAULT_LOG_LEVEL="debug" +fi +AC_SUBST(DEFAULT_LOG_LEVEL) + +# Load only one of rrdtool, network, csv in the default config. +LOAD_PLUGIN_RRDTOOL="" +LOAD_PLUGIN_NETWORK="" +LOAD_PLUGIN_CSV="" + +AC_MSG_CHECKING([which default write plugin to load]) +default_write_plugin="none" +if test "x$enable_rrdtool" = "xyes" +then + default_write_plugin="rrdtool" +else + LOAD_PLUGIN_RRDTOOL="##" +fi + +if test "x$enable_network" = "xyes" +then + if test "x$default_write_plugin" = "xnone" + then + default_write_plugin="network" + else + LOAD_PLUGIN_NETWORK="#" + fi +else + LOAD_PLUGIN_NETWORK="##" +fi + +if test "x$enable_csv" = "xyes" +then + if test "x$default_write_plugin" = "xnone" + then + default_write_plugin="csv" + else + LOAD_PLUGIN_CSV="#" + fi +else + LOAD_PLUGIN_CSV="##" +fi +AC_MSG_RESULT([$default_write_plugin]) + +AC_SUBST(LOAD_PLUGIN_RRDTOOL) +AC_SUBST(LOAD_PLUGIN_NETWORK) +AC_SUBST(LOAD_PLUGIN_CSV) + dnl ip_vs.h if test "x$ac_system" = "xLinux" \ && test "x$have_net_ip_vs_h$have_ip_vs_h" = "xnono" @@ -3167,7 +3576,7 @@ AC_SUBST(LCC_VERSION_STRING) AC_CONFIG_FILES(src/libcollectdclient/lcc_features.h) -AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libiptc/Makefile src/libcollectdclient/Makefile src/libcollectdclient/libcollectdclient.pc src/liboconfig/Makefile src/liboping/Makefile bindings/Makefile) +AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libiptc/Makefile src/libcollectdclient/Makefile src/libcollectdclient/libcollectdclient.pc src/liboconfig/Makefile bindings/Makefile) if test "x$with_librrd" = "xyes" \ && test "x$librrd_threadsafe" != "xyes" @@ -3175,12 +3584,6 @@ then with_librrd="yes (warning: librrd is not thread-safe)" fi -if test "x$with_liboping" = "xyes" \ - && test "x$with_own_liboping" = "xyes" -then - with_liboping="yes (shipped version)" -fi - if test "x$with_libiptc" = "xyes" -a "x$with_own_libiptc" = "xyes" then with_libiptc="yes (shipped version)" @@ -3208,6 +3611,7 @@ Configuration: libesmtp . . . . . . $with_libesmtp libiokit . . . . . . $with_libiokit libiptc . . . . . . . $with_libiptc + libjvm . . . . . . . $with_java libkstat . . . . . . $with_kstat libkvm . . . . . . . $with_libkvm libmysql . . . . . . $with_libmysql @@ -3256,12 +3660,14 @@ Configuration: entropy . . . . . . . $enable_entropy exec . . . . . . . . $enable_exec filecount . . . . . . $enable_filecount + gmond . . . . . . . . $enable_gmond hddtemp . . . . . . . $enable_hddtemp interface . . . . . . $enable_interface ipmi . . . . . . . . $enable_ipmi iptables . . . . . . $enable_iptables ipvs . . . . . . . . $enable_ipvs irq . . . . . . . . . $enable_irq + java . . . . . . . . $enable_java libvirt . . . . . . . $enable_libvirt load . . . . . . . . $enable_load logfile . . . . . . . $enable_logfile @@ -3269,6 +3675,7 @@ Configuration: match_timediff . . . $enable_match_timediff match_value . . . . . $enable_match_value mbmon . . . . . . . . $enable_mbmon + memcachec . . . . . . $enable_memcachec memcached . . . . . . $enable_memcached memory . . . . . . . $enable_memory multimeter . . . . . $enable_multimeter @@ -3289,6 +3696,7 @@ Configuration: postgresql . . . . . $enable_postgresql powerdns . . . . . . $enable_powerdns processes . . . . . . $enable_processes + protocols . . . . . . $enable_protocols rrdcached . . . . . . $enable_rrdcached rrdtool . . . . . . . $enable_rrdtool sensors . . . . . . . $enable_sensors @@ -3296,6 +3704,7 @@ Configuration: snmp . . . . . . . . $enable_snmp swap . . . . . . . . $enable_swap syslog . . . . . . . $enable_syslog + table . . . . . . . . $enable_table tail . . . . . . . . $enable_tail tape . . . . . . . . $enable_tape target_notification . $enable_target_notification @@ -3303,8 +3712,10 @@ Configuration: target_set . . . . . $enable_target_set tcpconns . . . . . . $enable_tcpconns teamspeak2 . . . . . $enable_teamspeak2 + ted . . . . . . . . . $enable_ted thermal . . . . . . . $enable_thermal unixsock . . . . . . $enable_unixsock + uptime . . . . . . . $enable_uptime users . . . . . . . . $enable_users uuid . . . . . . . . $enable_uuid vmem . . . . . . . . $enable_vmem diff --git a/contrib/collectd-unixsock.py b/contrib/collectd-unixsock.py new file mode 100644 index 00000000..2d7430d2 --- /dev/null +++ b/contrib/collectd-unixsock.py @@ -0,0 +1,111 @@ +#-*- coding: ISO-8859-1 -*- +# collect.py: the python collectd-unixsock module. +# +# Requires collectd to be configured with the unixsock plugin, like so: +# +# LoadPlugin unixsock +# +# SocketFile "/var/run/collectd-unixsock" +# SocketPerms "0775" +# +# +# Copyright (C) 2008 Clay Loveless +# +# This software is provided 'as-is', without any express or implied +# warranty. In no event will the author be held liable for any damages +# arising from the use of this software. +# +# Permission is granted to anyone to use this software for any purpose, +# including commercial applications, and to alter it and redistribute it +# freely, subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not +# claim that you wrote the original software. If you use this software +# in a product, an acknowledgment in the product documentation would be +# appreciated but is not required. +# 2. Altered source versions must be plainly marked as such, and must not be +# misrepresented as being the original software. +# 3. This notice may not be removed or altered from any source distribution. + +import socket, string + +class Collect(object): + + def __init__(self, path='/var/run/collectd-unixsock'): + self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self._path = path + self._sock.connect(self._path) + + def list(self): + numvalues = self._cmd('LISTVAL') + lines = [] + if numvalues: + lines = self._readlines(numvalues) + return lines + + def get(self, val, flush=True): + numvalues = self._cmd('GETVAL "' + val + '"') + lines = [] + if numvalues: + lines = self._readlines(numvalues) + if flush: + self._cmd('FLUSH identifier="' + val + '"') + return lines + + def _cmd(self, c): + self._sock.send(c + "\n") + stat = string.split(self._readline()) + status = int(stat[0]) + if status: + return status + return False + + ''' + _readline and _readlines methods borrowed from the _fileobject class + in sockets.py, tweaked a little bit for use in the collectd context. + ''' + def _readline(self): + data = '' + buf = [] + recv = self._sock.recv + while data != "\n": + data = recv(1) + if not data: + break + if data != "\n": + buf.append(data) + return ''.join(buf) + + def _readlines(self, sizehint=0): + total = 0 + list = [] + while True: + line = self._readline() + if not line: + break + list.append(line) + total = len(list) + if sizehint and total >= sizehint: + break + return list + + def __del__(self): + self._sock.close() + + + +if __name__ == '__main__': + + ''' + Example usage: + Collect values from socket and dump to STDOUT. + ''' + + c = Collect('/var/run/collectd-unixsock') + list = c.list() + + for val in list: + stamp, key = string.split(val) + glines = c.get(key) + print stamp + ' ' + key + ' ' + ', '.join(glines) + diff --git a/contrib/collection3/etc/collection.conf b/contrib/collection3/etc/collection.conf index 9497d8e3..2511221f 100644 --- a/contrib/collection3/etc/collection.conf +++ b/contrib/collection3/etc/collection.conf @@ -277,7 +277,7 @@ GraphWidth 400 Module GenericStacked DataSources value - RRDTitle "MySQL commands" + RRDTitle "MySQL commands ({plugin_instance})" RRDVerticalLabel "Invocations" RRDFormat "%6.2lf" @@ -361,7 +361,7 @@ GraphWidth 400 Module GenericStacked DataSources value - RRDTitle "MySQL handler" + RRDTitle "MySQL handler ({plugin_instance})" RRDVerticalLabel "Invocations" RRDFormat "%6.2lf" DSName commit commit @@ -391,7 +391,7 @@ GraphWidth 400 DataSources rx tx DSName rx RX DSName tx TX - RRDTitle "MySQL Traffic" + RRDTitle "MySQL Traffic ({plugin_instance})" RRDVerticalLabel "Bits per second" RRDFormat "%5.1lf%s" Scale 8 diff --git a/src/Makefile.am b/src/Makefile.am index f81fbbde..9b85144f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,9 +5,6 @@ endif if BUILD_WITH_OWN_LIBOCONFIG SUBDIRS += liboconfig endif -if BUILD_WITH_OWN_LIBOPING -SUBDIRS += liboping -endif if COMPILER_IS_GCC AM_CFLAGS = -Wall -Werror @@ -35,6 +32,7 @@ collectd_SOURCES = collectd.c collectd.h \ utils_avltree.c utils_avltree.h \ utils_cache.c utils_cache.h \ utils_complain.c utils_complain.h \ + utils_heap.c utils_heap.h \ utils_ignorelist.c utils_ignorelist.h \ utils_llist.c utils_llist.h \ utils_parse_option.c utils_parse_option.h \ @@ -90,11 +88,11 @@ collectdmon_SOURCES = collectdmon.c collectdmon_CPPFLAGS = $(AM_CPPFLAGS) collectd_nagios_SOURCES = collectd-nagios.c -collectd_nagios_LDFLAGS = +collectd_nagios_LDADD = if BUILD_WITH_LIBSOCKET -collectd_nagios_LDFLAGS += -lsocket +collectd_nagios_LDADD += -lsocket endif -collectd_nagios_LDADD = libcollectdclient/libcollectdclient.la +collectd_nagios_LDADD += libcollectdclient/libcollectdclient.la collectd_nagios_DEPENDENCIES = libcollectdclient/libcollectdclient.la pkglib_LTLIBRARIES = @@ -117,8 +115,9 @@ if BUILD_PLUGIN_APCUPS pkglib_LTLIBRARIES += apcups.la apcups_la_SOURCES = apcups.c apcups_la_LDFLAGS = -module -avoid-version +apcups_la_LIBADD = if BUILD_WITH_LIBSOCKET -apcups_la_LDFLAGS += -lsocket +apcups_la_LIBADD += -lsocket endif collectd_LDADD += "-dlopen" apcups.la collectd_DEPENDENCIES += apcups.la @@ -127,7 +126,8 @@ endif if BUILD_PLUGIN_APPLE_SENSORS pkglib_LTLIBRARIES += apple_sensors.la apple_sensors_la_SOURCES = apple_sensors.c -apple_sensors_la_LDFLAGS = -module -avoid-version -lIOKit +apple_sensors_la_LDFLAGS = -module -avoid-version +apple_sensors_la_LIBADD = -lIOKit collectd_LDADD += "-dlopen" apple_sensors.la collectd_DEPENDENCIES += apple_sensors.la endif @@ -147,8 +147,9 @@ if BUILD_PLUGIN_BATTERY pkglib_LTLIBRARIES += battery.la battery_la_SOURCES = battery.c battery_la_LDFLAGS = -module -avoid-version +battery_la_LIBADD = if BUILD_WITH_LIBIOKIT -battery_la_LDFLAGS += -lIOKit +battery_la_LIBADD += -lIOKit endif collectd_LDADD += "-dlopen" battery.la collectd_DEPENDENCIES += battery.la @@ -172,10 +173,10 @@ cpu_la_CFLAGS = $(AM_CFLAGS) cpu_la_LDFLAGS = -module -avoid-version cpu_la_LIBADD = if BUILD_WITH_LIBKSTAT -cpu_la_LDFLAGS += -lkstat +cpu_la_LIBADD += -lkstat endif if BUILD_WITH_LIBDEVINFO -cpu_la_LDFLAGS += -ldevinfo +cpu_la_LIBADD += -ldevinfo endif if BUILD_WITH_LIBSTATGRAB cpu_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS) @@ -241,13 +242,13 @@ disk_la_CFLAGS = $(AM_CFLAGS) disk_la_LDFLAGS = -module -avoid-version disk_la_LIBADD = if BUILD_WITH_LIBKSTAT -disk_la_LDFLAGS += -lkstat +disk_la_LIBADD += -lkstat endif if BUILD_WITH_LIBDEVINFO -disk_la_LDFLAGS += -ldevinfo +disk_la_LIBADD += -ldevinfo endif if BUILD_WITH_LIBIOKIT -disk_la_LDFLAGS += -lIOKit +disk_la_LIBADD += -lIOKit endif if BUILD_WITH_LIBSTATGRAB disk_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS) @@ -260,7 +261,8 @@ endif if BUILD_PLUGIN_DNS pkglib_LTLIBRARIES += dns.la dns_la_SOURCES = dns.c utils_dns.c utils_dns.h -dns_la_LDFLAGS = -module -avoid-version -lpcap -lpthread +dns_la_LDFLAGS = -module -avoid-version +dns_la_LIBADD = -lpcap -lpthread collectd_LDADD += "-dlopen" dns.la collectd_DEPENDENCIES += dns.la endif @@ -269,9 +271,7 @@ if BUILD_PLUGIN_EMAIL pkglib_LTLIBRARIES += email.la email_la_SOURCES = email.c email_la_LDFLAGS = -module -avoid-version -if BUILD_WITH_LIBPTHREAD -email_la_LDFLAGS += -lpthread -endif +email_la_LIBADD = -lpthread collectd_LDADD += "-dlopen" email.la collectd_DEPENDENCIES += email.la endif @@ -290,9 +290,7 @@ exec_la_SOURCES = exec.c \ utils_cmd_putnotif.c utils_cmd_putnotif.h \ utils_cmd_putval.c utils_cmd_putval.h exec_la_LDFLAGS = -module -avoid-version -if BUILD_WITH_LIBPTHREAD -exec_la_LDFLAGS += -lpthread -endif +exec_la_LIBADD = -lpthread collectd_LDADD += "-dlopen" exec.la collectd_DEPENDENCIES += exec.la endif @@ -305,12 +303,23 @@ collectd_LDADD += "-dlopen" filecount.la collectd_DEPENDENCIES += filecount.la endif +if BUILD_PLUGIN_GMOND +pkglib_LTLIBRARIES += gmond.la +gmond_la_SOURCES = gmond.c +gmond_la_CPPFLAGS = $(AM_CPPFLAGS) $(GANGLIA_CPPFLAGS) +gmond_la_LDFLAGS = -module -avoid-version $(GANGLIA_LDFLAGS) +gmond_la_LIBADD = $(GANGLIA_LIBS) +collectd_LDADD += "-dlopen" gmond.la +collectd_DEPENDENCIES += gmond.la +endif + if BUILD_PLUGIN_HDDTEMP pkglib_LTLIBRARIES += hddtemp.la hddtemp_la_SOURCES = hddtemp.c hddtemp_la_LDFLAGS = -module -avoid-version +hddtemp_la_LIBADD = if BUILD_WITH_LIBSOCKET -hddtemp_la_LDFLAGS += -lsocket +hddtemp_la_LIBADD += -lsocket endif collectd_LDADD += "-dlopen" hddtemp.la collectd_DEPENDENCIES += hddtemp.la @@ -329,10 +338,10 @@ interface_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS) interface_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS) else if BUILD_WITH_LIBKSTAT -interface_la_LDFLAGS += -lkstat +interface_la_LIBADD += -lkstat endif if BUILD_WITH_LIBDEVINFO -interface_la_LDFLAGS += -ldevinfo +interface_la_LIBADD += -ldevinfo endif # BUILD_WITH_LIBDEVINFO endif # !BUILD_WITH_LIBSTATGRAB endif # BUILD_PLUGIN_INTERFACE @@ -345,7 +354,7 @@ if BUILD_WITH_OWN_LIBIPTC iptables_la_LIBADD = libiptc/libiptc.la iptables_la_DEPENDENCIES = libiptc/libiptc.la else -iptables_la_LDFLAGS += -liptc +iptables_la_LIBADD = -liptc endif collectd_LDADD += "-dlopen" iptables.la collectd_DEPENDENCIES += iptables.la @@ -378,6 +387,17 @@ collectd_LDADD += "-dlopen" irq.la collectd_DEPENDENCIES += irq.la endif +if BUILD_PLUGIN_JAVA +pkglib_LTLIBRARIES += java.la +java_la_SOURCES = java.c +java_la_CPPFLAGS = $(AM_CPPFLAGS) $(JAVA_CPPFLAGS) +java_la_CFLAGS = $(AM_CFLAGS) $(JAVA_CFLAGS) +java_la_LDFLAGS = -module -avoid-version $(JAVA_LDFLAGS) +java_la_LIBADD = $(JAVA_LIBS) +collectd_LDADD += "-dlopen" java.la +collectd_DEPENDENCIES += java.la +endif + if BUILD_PLUGIN_LIBVIRT pkglib_LTLIBRARIES += libvirt.la libvirt_la_SOURCES = libvirt.c @@ -439,20 +459,31 @@ if BUILD_PLUGIN_MBMON pkglib_LTLIBRARIES += mbmon.la mbmon_la_SOURCES = mbmon.c mbmon_la_LDFLAGS = -module -avoid-version +mbmon_la_LIBADD = if BUILD_WITH_LIBSOCKET -mbmon_la_LDFLAGS += -lsocket +mbmon_la_LIBADD += -lsocket endif collectd_LDADD += "-dlopen" mbmon.la collectd_DEPENDENCIES += mbmon.la endif +if BUILD_PLUGIN_MEMCACHEC +pkglib_LTLIBRARIES += memcachec.la +memcachec_la_SOURCES = memcachec.c +memcachec_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBMEMCACHED_LDFLAGS) +memcachec_la_CPPFLAGS = $(BUILD_WITH_LIBMEMCACHED_CPPFLAGS) +memcachec_la_LIBADD = $(BUILD_WITH_LIBMEMCACHED_LIBS) +collectd_LDADD += "-dlopen" memcachec.la +collectd_DEPENDENCIES += memcachec.la +endif + if BUILD_PLUGIN_MEMCACHED pkglib_LTLIBRARIES += memcached.la memcached_la_SOURCES = memcached.c memcached_la_LDFLAGS = -module -avoid-version memcached_la_LIBADD = if BUILD_WITH_LIBSOCKET -memcached_la_LDFLAGS += -lsocket +memcached_la_LIBADD += -lsocket endif collectd_LDADD += "-dlopen" memcached.la collectd_DEPENDENCIES += memcached.la @@ -513,12 +544,16 @@ endif if BUILD_PLUGIN_NETWORK pkglib_LTLIBRARIES += network.la network_la_SOURCES = network.c network.h +network_la_CPPFLAGS = $(AM_CPPFLAGS) network_la_LDFLAGS = -module -avoid-version +network_la_LIBADD = -lpthread if BUILD_WITH_LIBSOCKET -network_la_LDFLAGS += -lsocket +network_la_LIBADD += -lsocket endif -if BUILD_WITH_LIBPTHREAD -network_la_LDFLAGS += -lpthread +if BUILD_WITH_LIBGCRYPT +network_la_CPPFLAGS += $(GCRYPT_CPPFLAGS) +network_la_LDFLAGS += $(GCRYPT_LDFLAGS) +network_la_LIBADD += $(GCRYPT_LIBS) endif collectd_LDADD += "-dlopen" network.la collectd_DEPENDENCIES += network.la @@ -550,7 +585,8 @@ if BUILD_PLUGIN_NOTIFY_DESKTOP pkglib_LTLIBRARIES += notify_desktop.la notify_desktop_la_SOURCES = notify_desktop.c notify_desktop_la_CFLAGS = $(AM_CFLAGS) $(LIBNOTIFY_CFLAGS) -notify_desktop_la_LDFLAGS = -module -avoid-version $(LIBNOTIFY_LIBS) +notify_desktop_la_LDFLAGS = -module -avoid-version +notify_desktop_la_LIBADD = $(LIBNOTIFY_LIBS) collectd_LDADD += "-dlopen" notify_desktop.la collectd_DEPENDENCIES += notify_desktop.la endif @@ -558,7 +594,8 @@ endif if BUILD_PLUGIN_NOTIFY_EMAIL pkglib_LTLIBRARIES += notify_email.la notify_email_la_SOURCES = notify_email.c -notify_email_la_LDFLAGS = -L/usr/local/lib -lesmtp -lssl -lcrypto -pthread -module -avoid-version +notify_email_la_LDFLAGS = -module -avoid-version +notify_email_la_LIBADD = -lesmtp -lssl -lcrypto -lpthread -ldl collectd_LDADD += "-dlopen" notify_email.la collectd_DEPENDENCIES += notify_email.la endif @@ -567,8 +604,9 @@ if BUILD_PLUGIN_NTPD pkglib_LTLIBRARIES += ntpd.la ntpd_la_SOURCES = ntpd.c ntpd_la_LDFLAGS = -module -avoid-version +ntpd_la_LIBADD = if BUILD_WITH_LIBSOCKET -ntpd_la_LDFLAGS += -lsocket +ntpd_la_LIBADD += -lsocket endif collectd_LDADD += "-dlopen" ntpd.la collectd_DEPENDENCIES += ntpd.la @@ -578,7 +616,8 @@ if BUILD_PLUGIN_NUT pkglib_LTLIBRARIES += nut.la nut_la_SOURCES = nut.c nut_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBUPSCLIENT_CFLAGS) -nut_la_LDFLAGS = -module -avoid-version -lpthread $(BUILD_WITH_LIBUPSCLIENT_LIBS) +nut_la_LDFLAGS = -module -avoid-version +nut_la_LIBADD = -lpthread $(BUILD_WITH_LIBUPSCLIENT_LIBS) collectd_LDADD += "-dlopen" nut.la collectd_DEPENDENCIES += nut.la endif @@ -638,13 +677,9 @@ endif if BUILD_PLUGIN_PING pkglib_LTLIBRARIES += ping.la ping_la_SOURCES = ping.c -ping_la_LDFLAGS = -module -avoid-version -if BUILD_WITH_OWN_LIBOPING -ping_la_LIBADD = liboping/liboping.la -ping_la_DEPENDENCIES = liboping/liboping.la -else -ping_la_LDFLAGS += -loping -endif +ping_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBOPING_CPPFLAGS) +ping_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBOPING_LDFLAGS) +ping_la_LIBADD = -loping collectd_LDADD += "-dlopen" ping.la collectd_DEPENDENCIES += ping.la endif @@ -655,7 +690,8 @@ postgresql_la_SOURCES = postgresql.c \ utils_db_query.c utils_db_query.h postgresql_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBPQ_CPPFLAGS) postgresql_la_LDFLAGS = -module -avoid-version \ - $(BUILD_WITH_LIBPQ_LDFLAGS) -lpq + $(BUILD_WITH_LIBPQ_LDFLAGS) +postgresql_la_LIBADD = -lpq collectd_LDADD += "-dlopen" postgresql.la collectd_DEPENDENCIES += postgresql.la endif @@ -680,6 +716,14 @@ processes_la_LIBADD += -lkvm endif endif +if BUILD_PLUGIN_PROTOCOLS +pkglib_LTLIBRARIES += protocols.la +protocols_la_SOURCES = protocols.c +protocols_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" protocols.la +collectd_DEPENDENCIES += protocols.la +endif + if BUILD_PLUGIN_RRDCACHED pkglib_LTLIBRARIES += rrdcached.la rrdcached_la_SOURCES = rrdcached.c utils_rrdcreate.c utils_rrdcreate.h @@ -704,8 +748,8 @@ if BUILD_PLUGIN_SENSORS pkglib_LTLIBRARIES += sensors.la sensors_la_SOURCES = sensors.c sensors_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBSENSORS_CFLAGS) -sensors_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBSENSORS_LDFLAGS) -sensors_la_LIBADD = -lsensors +sensors_la_LDFLAGS = -module -avoid-version +sensors_la_LIBADD = $(BUILD_WITH_LIBSENSORS_LDFLAGS) collectd_LDADD += "-dlopen" sensors.la collectd_DEPENDENCIES += sensors.la endif @@ -766,6 +810,14 @@ collectd_LDADD += "-dlopen" syslog.la collectd_DEPENDENCIES += syslog.la endif +if BUILD_PLUGIN_TABLE +pkglib_LTLIBRARIES += table.la +table_la_SOURCES = table.c +table_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" table.la +collectd_DEPENDENCIES += table.la +endif + if BUILD_PLUGIN_TAIL pkglib_LTLIBRARIES += tail.la tail_la_SOURCES = tail.c @@ -777,7 +829,8 @@ endif if BUILD_PLUGIN_TAPE pkglib_LTLIBRARIES += tape.la tape_la_SOURCES = tape.c -tape_la_LDFLAGS = -module -avoid-version -lkstat -ldevinfo +tape_la_LDFLAGS = -module -avoid-version +tape_la_LIBADD = -lkstat -ldevinfo collectd_LDADD += "-dlopen" tape.la collectd_DEPENDENCIES += tape.la endif @@ -826,6 +879,14 @@ collectd_LDADD += "-dlopen" teamspeak2.la collectd_DEPENDENCIES += teamspeak2.la endif +if BUILD_PLUGIN_TED +pkglib_LTLIBRARIES += ted.la +ted_la_SOURCES = ted.c +ted_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" ted.la +collectd_DEPENDENCIES += ted.la +endif + if BUILD_PLUGIN_THERMAL pkglib_LTLIBRARIES += thermal.la thermal_la_SOURCES = thermal.c @@ -842,11 +903,25 @@ unixsock_la_SOURCES = unixsock.c \ utils_cmd_listval.h utils_cmd_listval.c \ utils_cmd_putval.h utils_cmd_putval.c \ utils_cmd_putnotif.h utils_cmd_putnotif.c -unixsock_la_LDFLAGS = -module -avoid-version -lpthread +unixsock_la_LDFLAGS = -module -avoid-version +unixsock_la_LIBADD = -lpthread collectd_LDADD += "-dlopen" unixsock.la collectd_DEPENDENCIES += unixsock.la endif +if BUILD_PLUGIN_UPTIME +pkglib_LTLIBRARIES += uptime.la +uptime_la_SOURCES = uptime.c +uptime_la_CFLAGS = $(AM_CFLAGS) +uptime_la_LDFLAGS = -module -avoid-version +uptime_la_LIBADD = +if BUILD_WITH_LIBKSTAT +uptime_la_LIBADD += -lkstat +endif +collectd_LDADD += "-dlopen" uptime.la +collectd_DEPENDENCIES += uptime.la +endif + if BUILD_PLUGIN_USERS pkglib_LTLIBRARIES += users.la users_la_SOURCES = users.c @@ -899,25 +974,41 @@ if BUILD_PLUGIN_XMMS pkglib_LTLIBRARIES += xmms.la xmms_la_SOURCES = xmms.c xmms_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBXMMS_CFLAGS) -xmms_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBXMMS_LIBS) +xmms_la_LDFLAGS = -module -avoid-version +xmms_la_LIBADD = $(BUILD_WITH_LIBXMMS_LIBS) collectd_LDADD += "-dlopen" xmms.la collectd_DEPENDENCIES += xmms.la endif -dist_man_MANS = collectd.1 collectd-nagios.1 collectd.conf.5 \ - collectd-email.5 collectd-exec.5 collectd-perl.5 \ - collectd-snmp.5 collectd-unixsock.5 collectdmon.1 \ +dist_man_MANS = collectd.1 \ + collectd.conf.5 \ + collectd-email.5 \ + collectd-exec.5 \ + collectd-java.5 + collectdmon.1 \ + collectd-nagios.1 \ + collectd-perl.5 \ + collectd-snmp.5 \ + collectd-unixsock.5 \ types.db.5 #collectd_1_SOURCES = collectd.pod EXTRA_DIST = types.db -EXTRA_DIST += collectd-email.pod collectd-exec.pod collectd-nagios.pod \ - collectd-perl.pod collectd-snmp.pod collectd-unixsock.pod \ - collectd.conf.pod collectd.pod collectdmon.pod types.db.pod \ - postgresql_default.conf +EXTRA_DIST += collectd.conf.pod \ + collectd-email.pod \ + collectd-exec.pod \ + collectd-java.pod \ + collectdmon.pod \ + collectd-nagios.pod \ + collectd-perl.pod \ + collectd.pod \ + collectd-snmp.pod \ + collectd-unixsock.pod \ + postgresql_default.conf \ + types.db.pod .pod.1: pod2man --release=$(VERSION) --center=$(PACKAGE) $< \ diff --git a/src/bind.c b/src/bind.c index c0b18323..783d2b05 100644 --- a/src/bind.c +++ b/src/bind.c @@ -239,19 +239,6 @@ static int memsummary_translation_table_length = STATIC_ARRAY_SIZE (memsummary_translation_table); /* }}} */ -static void remove_special (char *buffer, size_t buffer_size) /* {{{ */ -{ - size_t i; - - for (i = 0; i < buffer_size; i++) - { - if (buffer[i] == 0) - return; - if ((!isalnum ((int) buffer[i])) && (buffer[i] != '-')) - buffer[i] = '_'; - } -} /* }}} void remove_special */ - static void submit (time_t ts, const char *plugin_instance, /* {{{ */ const char *type, const char *type_instance, value_t value) { @@ -268,13 +255,13 @@ static void submit (time_t ts, const char *plugin_instance, /* {{{ */ if (plugin_instance) { sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance)); - remove_special (vl.plugin_instance, sizeof (vl.plugin_instance)); + replace_special (vl.plugin_instance, sizeof (vl.plugin_instance)); } sstrncpy(vl.type, type, sizeof(vl.type)); if (type_instance) { sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); - remove_special (vl.plugin_instance, sizeof (vl.plugin_instance)); + replace_special (vl.plugin_instance, sizeof (vl.plugin_instance)); } plugin_dispatch_values(&vl); } /* }}} void submit */ diff --git a/src/collectd-java.pod b/src/collectd-java.pod new file mode 100644 index 00000000..f441c82a --- /dev/null +++ b/src/collectd-java.pod @@ -0,0 +1,555 @@ +=head1 NAME + +collectd-java - Documentation of collectd's "java plugin" + +=head1 SYNOPSIS + + LoadPlugin "java" + + JVMArg "-verbose:jni" + JVMArg "-Djava.class.path=/opt/collectd/lib/collectd/bindings/java" + + LoadPlugin "org.collectd.java.Foobar" + + # To be parsed by the plugin + + + +=head1 DESCRIPTION + +The I plugin embeds a I (JVM) into I and +provides a Java interface to part of collectd's API. This makes it possible to +write additions to the daemon in Java. + +This plugin is similar in nature to, but shares no code with, the I +plugin by Sebastian Harl, see L for details. + +=head1 CONFIGURATION + +A short outline of this plugin's configuration can be seen in L<"SYNOPSIS"> +above. For a complete list of all configuration options and their semantics +please read L>. + +=head1 OVERVIEW + +When writing additions for collectd in Java, the underlying C base is mostly +hidden from you. All complex data types are converted to their Java counterparts +before they're passed to your functions. These Java classes reside in the +I namespace. + +The I plugin will create one object of each class configured with the +B option. The constructor of this class can then register "callback +methods", i.Ee. methods that will be called by the daemon when +appropriate. + +The available classes are: + +=over 4 + +=item B + +All API functions exported to Java are implemented as static functions of this +class. See L<"EXPORTED API FUNCTIONS"> below. + +=item B + +Corresponds to C, defined in F. + +=item B + +Corresponds to C, defined in F. + +=item B + +Corresponds to C, defined in F. + +=item B + +Corresponds to C, defined in F. + +=item B + +Corresponds to C, defined in F. + +=item B + +Corresponds to C, defined in F. + +=back + +In the remainder of this document, we'll use the short form of these names, for +example B. In order to be able to use these abbreviated names, you +need to B the classes. + +=head1 EXPORTED API FUNCTIONS + +All collectd API functions that are available to Java plugins are implemented +as Istatic> functions of the B class. This makes +calling these functions pretty straight forward. For example, to send an error +message to the daemon, you'd do something like this: + + Collectd.logError ("That wasn't chicken!"); + +The following are the currently exported functions. + +=head2 registerConfig + +Signature: I B (I name, +I object); + +Registers the B function of I with the daemon. + +Returns zero upon success and non-zero when an error occurred. + +See L<"config callback"> below. + +=head2 registerInit + +Signature: I B (I name, +I object); + +Registers the B function of I with the daemon. + +Returns zero upon success and non-zero when an error occurred. + +See L<"init callback"> below. + +=head2 registerRead + +Signature: I B (I name, +I object) + +Registers the B function of I with the daemon. + +Returns zero upon success and non-zero when an error occurred. + +See L<"read callback"> below. + +=head2 registerWrite + +Signature: I B (I name, +I object) + +Registers the B function of I with the daemon. + +Returns zero upon success and non-zero when an error occurred. + +See L<"write callback"> below. + +=head2 registerFlush + +Signature: I B (I name, +I object) + +Registers the B function of I with the daemon. + +Returns zero upon success and non-zero when an error occurred. + +See L<"flush callback"> below. + +=head2 registerShutdown + +Signature: I B (I name, +I object); + +Registers the B function of I with the daemon. + +Returns zero upon success and non-zero when an error occurred. + +See L<"shutdown callback"> below. + +=head2 registerLog + +Signature: I B (I name, +I object); + +Registers the B function of I with the daemon. + +Returns zero upon success and non-zero when an error occurred. + +See L<"log callback"> below. + +=head2 registerNotification + +Signature: I B (I name, +I object); + +Registers the B function of I with the daemon. + +Returns zero upon success and non-zero when an error occurred. + +See L<"notification callback"> below. + +=head2 registerMatch + +Signature: I B (I name, +I object); + +Registers the B function of I with the daemon. + +Returns zero upon success and non-zero when an error occurred. + +See L<"match callback"> below. + +=head2 registerTarget + +Signature: I B (I name, +I object); + +Registers the B function of I with the daemon. + +Returns zero upon success and non-zero when an error occurred. + +See L<"target callback"> below. + +=head2 dispatchValues + +Signature: I B (I) + +Passes the values represented by the B object to the +C function of the daemon. The "data set" (or list of +"data sources") associated with the object are ignored, because +C will automatically lookup the required data set. It +is therefore absolutely okay to leave this blank. + +Returns zero upon success or non-zero upon failure. + +=head2 getDS + +Signature: I B (I) + +Returns the appropriate I or B if the type is not defined. + +=head2 logError + +Signature: I B (I) + +Sends a log message with severity B to the daemon. + +=head2 logWarning + +Signature: I B (I) + +Sends a log message with severity B to the daemon. + +=head2 logNotice + +Signature: I B (I) + +Sends a log message with severity B to the daemon. + +=head2 logInfo + +Signature: I B (I) + +Sends a log message with severity B to the daemon. + +=head2 logDebug + +Signature: I B (I) + +Sends a log message with severity B to the daemon. + +=head1 REGISTERING CALLBACKS + +When starting up, collectd creates an object of each configured class. The +constructor of this class should then register "callbacks" with the daemon, +using the appropriate static functions in B, +see L<"EXPORTED API FUNCTIONS"> above. To register a callback, the object being +passed to one of the register functions must implement an appropriate +interface, which are all in the B namespace. + +A constructor may register any number of these callbacks, even none. An object +without callback methods is never actively called by collectd, but may still +call the exported API functions. One could, for example, start a new thread in +the constructor and dispatch (submit to the daemon) values asynchronously, +whenever one is available. + +Each callback method is now explained in more detail: + +=head2 config callback + +Interface: B + +Signature: I B (I ci) + +This method is passed a B object, if both, method and +configuration, are available. B is the root of a tree representing +the configuration for this plugin. The root itself is the representation of the +BPluginE/E> block, so in next to all cases the children of the +root are the first interesting objects. + +To signal success, this method has to return zero. Anything else will be +considered an error condition and the plugin will be disabled entirely. + +See L<"registerConfig"> above. + +=head2 init callback + +Interface: B + +Signature: I B () + +This method is called after the configuration has been handled. It is +supposed to set up the plugin. e.Eg. start threads, open connections, or +check if can do anything useful at all. + +To signal success, this method has to return zero. Anything else will be +considered an error condition and the plugin will be disabled entirely. + +See L<"registerInit"> above. + +=head2 read callback + +Interface: B + +Signature: I B () + +This method is called periodically and is supposed to gather statistics in +whatever fashion. These statistics are represented as a B object and +sent to the daemon using L. + +To signal success, this method has to return zero. Anything else will be +considered an error condition and cause an appropriate message to be logged. +Currently, returning non-zero does not have any other effects. In particular, +Java "read"-methods are not suspended for increasing intervals like C +"read"-functions. + +See L<"registerRead"> above. + +=head2 write callback + +Interface: B + +Signature: I B (I vl) + +This method is called whenever a value is dispatched to the daemon. The +corresponding C "write"-functions are passed a C, so they can +decide which values are absolute values (gauge) and which are counter values. +To get the corresponding CDataSourceE>, call the B +method of the B object. + +To signal success, this method has to return zero. Anything else will be +considered an error condition and cause an appropriate message to be logged. + +See L<"registerWrite"> above. + +=head2 flush callback + +Interface: B + +Signature: I B (I timeout, I identifier) + +This method is called when the daemon received a flush command. This can either +be done using the C signal (see L) or using the I +plugin (see L). + +If I is greater than zero, only values older than this number of +seconds should be flushed. To signal that all values should be flushed +regardless of age, this argument is set to a negative number. + +The I specifies which value should be flushed. If it is not +possible to flush one specific value, flush all values. To signal that all +values should be flushed, this argument is set to I. + +To signal success, this method has to return zero. Anything else will be +considered an error condition and cause an appropriate message to be logged. + +See L<"registerFlush"> above. + +=head2 shutdown callback + +Interface: B + +Signature: I B () + +This method is called when the daemon is shutting down. You should not rely on +the destructor to clean up behind the object but use this function instead. + +To signal success, this method has to return zero. Anything else will be +considered an error condition and cause an appropriate message to be logged. + +See L<"registerShutdown"> above. + +=head2 log callback + +Interface: B + +Signature: I B (I severity, I message) + +This callback can be used to receive log messages from the daemon. + +The argument I is one of: + +=over 4 + +=item * + +org.collectd.api.Collectd.LOG_ERR + +=item * + +org.collectd.api.Collectd.LOG_WARNING + +=item * + +org.collectd.api.Collectd.LOG_NOTICE + +=item * + +org.collectd.api.Collectd.LOG_INFO + +=item * + +org.collectd.api.Collectd.LOG_DEBUG + +=back + +The function does not return any value. + +See L<"registerLog"> above. + +=head2 notification callback + +Interface: B + +Signature: I B (I n) + +This callback can be used to receive notifications from the daemon. + +To signal success, this method has to return zero. Anything else will be +considered an error condition and cause an appropriate message to be logged. + +See L<"registerNotification"> above. + +=head2 match callback + +The match (and target, see L<"target callback"> below) callbacks work a bit +different from the other callbacks above: You don't register a match callback +with the daemon directly, but you register a function which, when called, +creates an appropriate object. The object creating the "match" objects is +called "match factory". + +See L<"registerMatch"> above. + +=head3 Factory object + +Interface: B + +Signature: I B +(I ci); + +Called by the daemon to create "match" objects. + +Returns: A new object which implements the B interface. + +=head3 Match object + +Interface: B + +Signature: I B (I ds, I vl); + +Called when processing a chain to determine whether or not a I +matches. How values are matches is up to the implementing class. + +Has to return one of: + +=over 4 + +=item * + +B + +=item * + +B + +=back + +=head2 target callback + +The target (and match, see L<"match callback"> above) callbacks work a bit +different from the other callbacks above: You don't register a target callback +with the daemon directly, but you register a function which, when called, +creates an appropriate object. The object creating the "target" objects is +called "target factory". + +See L<"registerTarget"> above. + +=head3 Factory object + +Interface: B + +Signature: I B +(I ci); + +Called by the daemon to create "target" objects. + +Returns: A new object which implements the B +interface. + +=head3 Target object + +Interface: B + +Signature: I B (I ds, I vl); + +Called when processing a chain to perform some action. The action performed is +up to the implementing class. + +Has to return one of: + +=over 4 + +=item * + +B + +=item * + +B + +=item * + +B + +=back + +=head1 EXAMPLE + +This short example demonstrates how to register a read callback with the +daemon: + + import org.collectd.api.Collectd; + import org.collectd.api.ValueList; + + import org.collectd.api.CollectdReadInterface; + + public class Foobar implements CollectdReadInterface + { + public Foobar () + { + Collectd.registerRead ("Foobar", this); + } + + public int read () + { + ValueList vl; + + /* Do something... */ + + Collectd.dispatchValues (vl); + } + } + +=head1 SEE ALSO + +L, +L, +L, +L + +=head1 AUTHOR + +Florian Forster EoctoEatEverplant.orgE + diff --git a/src/collectd-perl.pod b/src/collectd-perl.pod index acb8abda..0f48ba58 100644 --- a/src/collectd-perl.pod +++ b/src/collectd-perl.pod @@ -297,6 +297,10 @@ there is a large number of predefined data-sets available in the B file which are automatically registered with collectd - see L for a description of the format of this file. +B: Using B to register a data-set is deprecated. Add +the new type to a custom L file instead. This functionality might +be removed in a future version of collectd. + If the I argument is any of the other types (B, B, ...) then I is expected to be a function name. If the name is not prefixed with the plugin's package name collectd will add it automatically. diff --git a/src/collectd-snmp.pod b/src/collectd-snmp.pod index d0acc06d..f34113d9 100644 --- a/src/collectd-snmp.pod +++ b/src/collectd-snmp.pod @@ -221,16 +221,11 @@ before using it here. =item B I -Collect data from this host every I seconds. This value needs to be a -multiple of the global B setting and, if it is not, will be rounded -B to one and a warning is logged in this case. So if your global -B is set to I<10> and you configure I<25> here, it's rounded down to -I<20>. By default the global B setting will be used. - -This option is meant for devices with not much CPU power, e.Eg. network -equipment such as switches, embedded devices, rack monitoring systems and so -on. Since the B of generated RRD files depends on this setting it's -wise to select a reasonable value once and never change it. +Collect data from this host every I seconds. This option is meant for +devices with not much CPU power, e.Eg. network equipment such as +switches, embedded devices, rack monitoring systems and so on. Since the +B of generated RRD files depends on this setting it's wise to select a +reasonable value once and never change it. =back diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 8a059122..6783a15c 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -4,6 +4,12 @@ # http://collectd.org/ # +############################################################################## +# Global # +#----------------------------------------------------------------------------# +# Global settings for the daemon. # +############################################################################## + #Hostname "localhost" FQDNLookup true #BaseDir "@prefix@/var/lib/@PACKAGE_NAME@" @@ -13,84 +19,113 @@ FQDNLookup true #Interval 10 #ReadThreads 5 -@BUILD_PLUGIN_LOGFILE_TRUE@LoadPlugin logfile -@BUILD_PLUGIN_SYSLOG_TRUE@LoadPlugin syslog +############################################################################## +# Logging # +#----------------------------------------------------------------------------# +# Plugins which provide logging functions should be loaded first, so log # +# messages generated when loading or configuring other plugins can be # +# accessed. # +############################################################################## + +@LOAD_PLUGIN_SYSLOG@LoadPlugin syslog +@LOAD_PLUGIN_LOGFILE@LoadPlugin logfile # -# LogLevel info +# LogLevel @DEFAULT_LOG_LEVEL@ # File STDOUT # Timestamp true # # -# LogLevel info -# - -@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache -@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups -@BUILD_PLUGIN_APPLE_SENSORS_TRUE@LoadPlugin apple_sensors -@BUILD_PLUGIN_ASCENT_TRUE@LoadPlugin ascent -@BUILD_PLUGIN_BATTERY_TRUE@LoadPlugin battery -@BUILD_PLUGIN_BIND_TRUE@LoadPlugin bind -@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu -@BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq -@BUILD_PLUGIN_CSV_TRUE@LoadPlugin csv -@BUILD_PLUGIN_CURL_TRUE@LoadPlugin curl -@BUILD_PLUGIN_DBI_TRUE@LoadPlugin dbi -@BUILD_PLUGIN_DF_TRUE@LoadPlugin df -@BUILD_PLUGIN_DISK_TRUE@LoadPlugin disk -@BUILD_PLUGIN_DNS_TRUE@LoadPlugin dns -@BUILD_PLUGIN_EMAIL_TRUE@LoadPlugin email -@BUILD_PLUGIN_ENTROPY_TRUE@LoadPlugin entropy -@BUILD_PLUGIN_EXEC_TRUE@LoadPlugin exec -@BUILD_PLUGIN_FILECOUNT_TRUE@LoadPlugin filecount -@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp -@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface -@BUILD_PLUGIN_IPTABLES_TRUE@LoadPlugin iptables -@BUILD_PLUGIN_IPMI_TRUE@LoadPlugin ipmi -@BUILD_PLUGIN_IPVS_TRUE@LoadPlugin ipvs -@BUILD_PLUGIN_IRQ_TRUE@LoadPlugin irq -@BUILD_PLUGIN_LIBVIRT_TRUE@LoadPlugin libvirt -@BUILD_PLUGIN_LOAD_TRUE@LoadPlugin load -@BUILD_PLUGIN_MBMON_TRUE@LoadPlugin mbmon -@BUILD_PLUGIN_MEMCACHED_TRUE@LoadPlugin memcached -@BUILD_PLUGIN_MEMORY_TRUE@LoadPlugin memory -@BUILD_PLUGIN_MULTIMETER_TRUE@LoadPlugin multimeter -@BUILD_PLUGIN_MYSQL_TRUE@LoadPlugin mysql -@BUILD_PLUGIN_NETLINK_TRUE@LoadPlugin netlink -@BUILD_PLUGIN_NETWORK_TRUE@LoadPlugin network -@BUILD_PLUGIN_NFS_TRUE@LoadPlugin nfs -@BUILD_PLUGIN_NGINX_TRUE@LoadPlugin nginx -@BUILD_PLUGIN_NOTIFY_DESKTOP_TRUE@LoadPlugin notify_desktop -@BUILD_PLUGIN_NOTIFY_EMAIL_TRUE@LoadPlugin notify_email -@BUILD_PLUGIN_NTPD_TRUE@LoadPlugin ntpd -@BUILD_PLUGIN_NUT_TRUE@LoadPlugin nut -@BUILD_PLUGIN_ONEWIRE_TRUE@LoadPlugin onewire -@BUILD_PLUGIN_OPENVPN_TRUE@LoadPlugin openvpn -@BUILD_PLUGIN_ORACLE_TRUE@LoadPlugin oracle -@BUILD_PLUGIN_PERL_TRUE@LoadPlugin perl -@BUILD_PLUGIN_PING_TRUE@LoadPlugin ping -@BUILD_PLUGIN_POSTGRESQL_TRUE@LoadPlugin postgresql -@BUILD_PLUGIN_POWERDNS_TRUE@LoadPlugin powerdns -@BUILD_PLUGIN_PROCESSES_TRUE@LoadPlugin processes -@BUILD_PLUGIN_RRDCACHED_TRUE@LoadPlugin rrdcached -@BUILD_PLUGIN_RRDTOOL_TRUE@LoadPlugin rrdtool -@BUILD_PLUGIN_SENSORS_TRUE@LoadPlugin sensors -@BUILD_PLUGIN_SERIAL_TRUE@LoadPlugin serial -@BUILD_PLUGIN_SNMP_TRUE@LoadPlugin snmp -@BUILD_PLUGIN_SWAP_TRUE@LoadPlugin swap -@BUILD_PLUGIN_TAIL_TRUE@LoadPlugin tail -@BUILD_PLUGIN_TAPE_TRUE@LoadPlugin tape -@BUILD_PLUGIN_TCPCONNS_TRUE@LoadPlugin tcpconns -@BUILD_PLUGIN_TEAMSPEAK2_TRUE@LoadPlugin teamspeak2 -@BUILD_PLUGIN_THERMAL_TRUE@LoadPlugin thermal -@BUILD_PLUGIN_UNIXSOCK_TRUE@LoadPlugin unixsock -@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users -#LoadPlugin uuid -@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem -@BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver -@BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless -@BUILD_PLUGIN_XMMS_TRUE@LoadPlugin xmms +# LogLevel @DEFAULT_LOG_LEVEL@ +# + +############################################################################## +# LoadPlugin section # +#----------------------------------------------------------------------------# +# Lines beginning with a single `#' belong to plugins which have been built # +# but are disabled by default. # +# # +# Lines begnning with `##' belong to plugins which have not been built due # +# to missing dependencies or because they have been deactivated explicitly. # +############################################################################## + +#@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache +#@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups +#@BUILD_PLUGIN_APPLE_SENSORS_TRUE@LoadPlugin apple_sensors +#@BUILD_PLUGIN_ASCENT_TRUE@LoadPlugin ascent +#@BUILD_PLUGIN_BATTERY_TRUE@LoadPlugin battery +#@BUILD_PLUGIN_BIND_TRUE@LoadPlugin bind +@BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu +#@BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq +@LOAD_PLUGIN_CSV@LoadPlugin csv +#@BUILD_PLUGIN_CURL_TRUE@LoadPlugin curl +#@BUILD_PLUGIN_DBI_TRUE@LoadPlugin dbi +#@BUILD_PLUGIN_DF_TRUE@LoadPlugin df +#@BUILD_PLUGIN_DISK_TRUE@LoadPlugin disk +#@BUILD_PLUGIN_DNS_TRUE@LoadPlugin dns +#@BUILD_PLUGIN_EMAIL_TRUE@LoadPlugin email +#@BUILD_PLUGIN_ENTROPY_TRUE@LoadPlugin entropy +#@BUILD_PLUGIN_EXEC_TRUE@LoadPlugin exec +#@BUILD_PLUGIN_FILECOUNT_TRUE@LoadPlugin filecount +#@BUILD_PLUGIN_GMOND_TRUE@LoadPlugin gmond +#@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp +@BUILD_PLUGIN_INTERFACE_TRUE@@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface +#@BUILD_PLUGIN_IPTABLES_TRUE@LoadPlugin iptables +#@BUILD_PLUGIN_IPMI_TRUE@LoadPlugin ipmi +#@BUILD_PLUGIN_IPVS_TRUE@LoadPlugin ipvs +#@BUILD_PLUGIN_IRQ_TRUE@LoadPlugin irq +#@BUILD_PLUGIN_JAVA_TRUE@LoadPlugin java +#@BUILD_PLUGIN_LIBVIRT_TRUE@LoadPlugin libvirt +@BUILD_PLUGIN_LOAD_TRUE@@BUILD_PLUGIN_LOAD_TRUE@LoadPlugin load +#@BUILD_PLUGIN_MBMON_TRUE@LoadPlugin mbmon +#@BUILD_PLUGIN_MEMCACHED_TRUE@LoadPlugin memcached +@BUILD_PLUGIN_MEMORY_TRUE@@BUILD_PLUGIN_MEMORY_TRUE@LoadPlugin memory +#@BUILD_PLUGIN_MULTIMETER_TRUE@LoadPlugin multimeter +#@BUILD_PLUGIN_MYSQL_TRUE@LoadPlugin mysql +#@BUILD_PLUGIN_NETLINK_TRUE@LoadPlugin netlink +@LOAD_PLUGIN_NETWORK@LoadPlugin network +#@BUILD_PLUGIN_NFS_TRUE@LoadPlugin nfs +#@BUILD_PLUGIN_NGINX_TRUE@LoadPlugin nginx +#@BUILD_PLUGIN_NOTIFY_DESKTOP_TRUE@LoadPlugin notify_desktop +#@BUILD_PLUGIN_NOTIFY_EMAIL_TRUE@LoadPlugin notify_email +#@BUILD_PLUGIN_NTPD_TRUE@LoadPlugin ntpd +#@BUILD_PLUGIN_NUT_TRUE@LoadPlugin nut +#@BUILD_PLUGIN_ONEWIRE_TRUE@LoadPlugin onewire +#@BUILD_PLUGIN_OPENVPN_TRUE@LoadPlugin openvpn +#@BUILD_PLUGIN_ORACLE_TRUE@LoadPlugin oracle +#@BUILD_PLUGIN_PERL_TRUE@LoadPlugin perl +#@BUILD_PLUGIN_PING_TRUE@LoadPlugin ping +#@BUILD_PLUGIN_POSTGRESQL_TRUE@LoadPlugin postgresql +#@BUILD_PLUGIN_POWERDNS_TRUE@LoadPlugin powerdns +#@BUILD_PLUGIN_PROCESSES_TRUE@LoadPlugin processes +#@BUILD_PLUGIN_PROTOCOLS_TRUE@LoadPlugin protocols +#@BUILD_PLUGIN_RRDCACHED_TRUE@LoadPlugin rrdcached +@LOAD_PLUGIN_RRDTOOL@LoadPlugin rrdtool +#@BUILD_PLUGIN_SENSORS_TRUE@LoadPlugin sensors +#@BUILD_PLUGIN_SERIAL_TRUE@LoadPlugin serial +#@BUILD_PLUGIN_SNMP_TRUE@LoadPlugin snmp +#@BUILD_PLUGIN_SWAP_TRUE@LoadPlugin swap +#@BUILD_PLUGIN_TAIL_TRUE@LoadPlugin tail +#@BUILD_PLUGIN_TAPE_TRUE@LoadPlugin tape +#@BUILD_PLUGIN_TCPCONNS_TRUE@LoadPlugin tcpconns +#@BUILD_PLUGIN_TEAMSPEAK2_TRUE@LoadPlugin teamspeak2 +#@BUILD_PLUGIN_THERMAL_TRUE@LoadPlugin thermal +#@BUILD_PLUGIN_UNIXSOCK_TRUE@LoadPlugin unixsock +#@BUILD_PLUGIN_UPTIME_TRUE@LoadPlugin uptime +#@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users +#@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid +#@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem +#@BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver +#@BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless +#@BUILD_PLUGIN_XMMS_TRUE@LoadPlugin xmms + +############################################################################## +# Plugin configuration # +#----------------------------------------------------------------------------# +# In this section configuration stubs for each plugin are provided. A desc- # +# ription of those options is available in the collectd.conf(5) manual page. # +############################################################################## # # URL "http://localhost/status?auto" @@ -210,11 +245,31 @@ FQDNLookup true # # -@BUILD_PLUGIN_HDDTEMP_TRUE@ -# Host "127.0.0.1" -# Port "7634" -@BUILD_PLUGIN_HDDTEMP_TRUE@ TranslateDevicename false -@BUILD_PLUGIN_HDDTEMP_TRUE@ +# +# MCReceiveFrom "239.2.11.71" "8649" +# +# Type "swap" +# TypeInstance "total" +# DataSource "value" +# +# +# Type "swap" +# TypeInstance "free" +# DataSource "value" +# +# + +# +# Host "127.0.0.1" +# Port "7634" +# +# #----------------------------------------------------------------# +# # `TranslateDevicename' enabled backwards compatibility behavior # +# # and is enabled by default. Setting this option to `false' is # +# # highly recommended. # +# #----------------------------------------------------------------# +# TranslateDevicename false +# # # Interface "eth0" @@ -232,6 +287,16 @@ FQDNLookup true # IgnoreSelected true # +# +# JVMArg "-verbose:jni" +# JVMArg "-Djava.class.path=/opt/collectd/lib/collectd/bindings/java" +# +# LoadPlugin "org.collectd.java.Foobar" +# +# # To be parsed by the plugin +# +# + # # Connection "xen:///" # RefreshInterval 60 @@ -253,10 +318,20 @@ FQDNLookup true # # -# Host "database.serv.er" -# User "db_user" -# Password "secret" -# Database "db_name" +# +# Host "database.serv.er" +# User "db_user" +# Password "secret" +# Database "db_name" +# MasterStats true +# +# +# +# Host "localhost" +# Socket "/var/run/mysql/mysqld.sock" +# SlaveStats true +# SlaveNotifications true +# # # @@ -268,15 +343,15 @@ FQDNLookup true # IgnoreSelected false # -# -# Server "ff18::efc0:4a42" "25826" -# Server "239.192.74.66" "25826" +@LOAD_PLUGIN_NETWORK@ +@LOAD_PLUGIN_NETWORK@ Server "ff18::efc0:4a42" "25826" +@LOAD_PLUGIN_NETWORK@ Server "239.192.74.66" "25826" # Listen "ff18::efc0:4a42" "25826" # Listen "239.192.74.66" "25826" # TimeToLive "128" # Forward false # CacheFlush 1800 -# +@LOAD_PLUGIN_NETWORK@ # # URL "http://localhost/status?auto" @@ -415,6 +490,11 @@ FQDNLookup true # Process "name" # +# +# Value "/^Tcp:/" +# IgnoreSelected false +# + # # DaemonAddress "unix:/tmp/rrdcached.sock" # DataDir "@prefix@/var/lib/@PACKAGE_NAME@/rrd" @@ -454,7 +534,7 @@ FQDNLookup true # Instance "IF-MIB::ifDescr" # Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets" # -# +# # # Address "192.168.0.2" # Version 1 @@ -527,13 +607,13 @@ FQDNLookup true # Verbose false # -# * * * * * * * * * * * * * -# * FILTER CONFIGURATION * -# * * * * * * * * * * * * * - -# The following configures collectd's filtering mechanism. Before changing -# anything in this section, please read the `FILTER CONFIGURATION' section in -# the collectd.conf(5) manual page. +############################################################################## +# Filter configuration # +#----------------------------------------------------------------------------# +# The following configures collectd's filtering mechanism. Before changing # +# anything in this section, please read the `FILTER CONFIGURATION' section # +# in the collectd.conf(5) manual page. # +############################################################################## # Load required matches: #@BUILD_PLUGIN_MATCH_REGEX_TRUE@LoadPlugin match_regex @@ -544,9 +624,11 @@ FQDNLookup true #@BUILD_PLUGIN_TARGET_NOTIFICATION_TRUE@LoadPlugin target_notification #@BUILD_PLUGIN_TARGET_REPLACE_TRUE@LoadPlugin target_replace #@BUILD_PLUGIN_TARGET_SET_TRUE@LoadPlugin target_set - -# The following block demonstrates the default behavior if no filtering is -# configured at all: All values will be sent to all available write plugins. + +#----------------------------------------------------------------------------# +# The following block demonstrates the default behavior if no filtering is # +# configured at all: All values will be sent to all available write plugins. # +#----------------------------------------------------------------------------# # # Target "write" diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 43946bdf..581da285 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -665,7 +665,7 @@ separated by dashes I<("-")>. Specifies the columns whose values will be used to create the "type-instance" for each row. If you specify more than one column, the value of all columns -will be join together with the dashes I<("-")> as separation character. +will be joined together with dashes I<("-")> as separation characters. The plugin itself does not check whether or not all built instances are different. It's your responsibility to assure that each is unique. This is @@ -962,6 +962,88 @@ Controls whether or not to recurse into subdirectories. Enabled by default. =back +=head2 Plugin C + +The I plugin received the multicast traffic sent by B, the +statistics collection daemon of Ganglia. Mappings for the standard "metrics" +are built-in, custom mappings may be added via B blocks, see below. + +Synopsis: + + + MCReceiveFrom "239.2.11.71" "8649" + + Type "swap" + TypeInstance "total" + DataSource "value" + + + Type "swap" + TypeInstance "free" + DataSource "value" + + + +The following metrics are built-in: + +=over 4 + +=item * + +load_one, load_five, load_fifteen + +=item * + +cpu_user, cpu_system, cpu_idle, cpu_nice, cpu_wio + +=item * + +mem_free, mem_shared, mem_buffers, mem_cached, mem_total + +=item * + +bytes_in, bytes_out + +=item * + +pkts_in, pkts_out + +=back + +Available configuration options: + +=over 4 + +=item B I [I] + +Sets sets the multicast group and UDP port to which to subscribe. + +Default: B<239.2.11.71>E/EB<8649> + +=item EB IE + +These blocks add a new metric conversion to the internal table. I, the +string argument to the B block, is the metric name as used by Ganglia. + +=over 4 + +=item B I + +Type to map this metric to. Required. + +=item B I + +Type-instance to use. Optional. + +=item B I + +Data source to map this metric to. If the configured type has exactly one data +source, this is optional. Otherwise the option is required. + +=back + +=back + =head2 Plugin C To get values from B collectd connects to B (127.0.0.1), @@ -1086,6 +1168,62 @@ and all other interrupts are collected. =back +=head2 Plugin C + +The I plugin makes it possible to write extensions for collectd in Java. +This section only discusses the syntax and semantic of the configuration +options. For more in-depth information on the I plugin, please read +L. + +Synopsis: + + + JVMArg "-verbose:jni" + JVMArg "-Djava.class.path=/opt/collectd/lib/collectd/bindings/java" + LoadPlugin "org.collectd.java.Foobar" + + # To be parsed by the plugin + + + +Available configuration options: + +=over 4 + +=item B I + +Argument that is to be passed to the I (JVM). This works +exactly the way the arguments to the I binary on the command line work. +Execute C--help> for details. + +Please note that B these options must appear B (i.Ee. above) +any other options! When another option is found, the JVM will be started and +later options will have to be ignored! + +=item B I + +Instantiates a new I object. The constructor of this object very +likely then registers one or more callback methods with the server. + +See L for details. + +When the first such option is found, the virtual machine (JVM) is created. This +means that all B options must appear before (i.Ee. above) all +B options! + +=item B I + +The entire block is passed to the Java plugin as an +I object. + +For this to work, the plugin has to register a configuration callback first, +see L. This means, that the B block +must appear after the appropriate B block. Also note, that I +depends on the (Java) plugin registering the callback and is completely +independent from the I argument passed to B. + +=back + =head2 Plugin C This plugin allows CPU, disk and network load to be collected for virtualized @@ -1237,19 +1375,50 @@ TCP-Port to connect to. Defaults to B<11211>. =head2 Plugin C -The C requires B to be installed. It connects to the -database when started and keeps the connection up as long as possible. When the -connection is interrupted for whatever reason it will try to re-connect. The -plugin will complaint loudly in case anything goes wrong. +The C requires B to be installed. It connects to +one or more databases when started and keeps the connection up as long as +possible. When the connection is interrupted for whatever reason it will try +to re-connect. The plugin will complaint loudly in case anything goes wrong. + +This plugin issues the MySQL C / C command +and collects information about MySQL network traffic, executed statements, +requests, the query cache and threads by evaluating the +C, C, C, C and C +return values. Please refer to the B, I<5.1.6. Server +Status Variables> for an explanation of these values. + +Optionally, master and slave statistics may be collected in a MySQL +replication setup. In that case, information about the synchronization state +of the nodes are collected by evaluating the C return value of the +C command and the C, +C and C return values of the +C command. See the B, +I<12.5.5.21 SHOW MASTER STATUS Syntax> and +I<12.5.5.31 SHOW SLAVE STATUS Syntax> for details. -This plugin issues the MySQL C command and collects information -about MySQL network traffic, executed statements, requests, the query cache -and threads by evaluating the C, C, -C, C and C return values. Please refer to the -B, I<5.1.6. Server Status Variables> for an -explanation of these values. +Synopsis: -Use the following options to configure the plugin: + + + Host "hostname" + User "username" + Password "password" + Port "3306" + MasterStats true + + + + Host "localhost" + Socket "/var/run/mysql/mysqld.sock" + SlaveStats true + SlaveNotifications true + + + +A B block defines one connection to a MySQL database. It accepts a +single argument which specifies the name of the database. None of the other +options are required. MySQL will use default values as documented in the +section "mysql_real_connect()" in the B. =over 4 @@ -1289,6 +1458,17 @@ only has any effect, if B is set to B (the default). Otherwise, use the B option above. See the documentation for the C function for details. +=item B I + +=item B I + +Enable the collection of master / slave statistics in a replication setup. + +=item B I + +If enabled, the plugin sends a notification if the replication slave I/O and / +or SQL threads are not running. + =back =head2 Plugin C @@ -1397,6 +1577,45 @@ The optional I argument sets the port to use. It can either be given using a numeric port number or a service name. If the argument is omitted the default port B<25826> is assumed. +Both, B and B can be used as block, too, to provide +configuration options for this socket only. For example: + + + + SecurityLevel "sign" + Secret "ohl0eQue" + + + +The following options are understood within BServerE> and +BListenE> blocks: + +=over 4 + +=item B B|B|B + +Set the security you require for network communication. When the security level +has been set to B, data sent over the network will be encrypted using +I and only encrypted data will be accepted when receiving. When set to +B, transmitted data is signed using I and only signed and +encrypted data is accepted when receiving. When set to B, data is sent +without any security and all data is accepted when receiving. + +This feature is only available if the I plugin was linked with +I. + +=item B I + +Sets a shared secret for this socket. All security levels except B +require this setting. For B this setting is only required if you want to +be able to decrypt encrypted data (B is set to B on the +client). + +This feature is only available if the I plugin was linked with +I. + +=back + =item B I<1-255> Set the time-to-live of sent packets. This applies to all, unicast and @@ -1633,6 +1852,11 @@ enables you to do that: By setting B to I the effect of B is inverted: All selected interfaces are ignored and all other interfaces are collected. +=item B I + +Sets the interval in which all sensors should be read. If not specified, the +global B setting is used. + =back B The C plugin is experimental, because it doesn't yet @@ -1741,6 +1965,13 @@ to collectd's plugin system. See L for its documentation. =head2 Plugin C +The I plugin starts a new thread which sends ICMP "ping" packets to the +configured hosts periodically and measures the network latency. Whenever the +C function of the plugin is called, it submits the average latency, the +standard deviation and the drop rate for each host. + +Available configuration options: + =over 4 =item B I @@ -1748,6 +1979,26 @@ to collectd's plugin system. See L for its documentation. Host to ping periodically. This option may be repeated several times to ping multiple hosts. +=item B I + +Sets the interval in which to send ICMP echo packets to the configured hosts. +This is B the interval in which statistics are queries from the plugin but +the interval in which the hosts are "pinged". Therefore, the setting here +should be smaller than or equal to the global B setting. Fractional +times, such as "1.24" are allowed. + +Default: B<1.0> + +=item B I + +Time to wait for a response from the host to which an ICMP packet had been +sent. If a reply was not received after I seconds, the host is assumed +to be down or the packet to be dropped. This setting must be smaller than the +B setting above for the plugin to work correctly. Fractional +arguments are accepted. + +Default: B<0.9> + =item B I<0-255> Sets the Time-To-Live of generated ICMP packets. @@ -2234,6 +2485,39 @@ slashes. =back +=head2 Plugin C + +Collects a lot of information about various network protocols, such as I, +I, I, etc. + +Available configuration options: + +=over 4 + +=item B I + +Selects whether or not to select a specific value. The string being matched is +of the form "I:I", where I will be used as the +plugin instance and I will be used as type instance. An example of +the string being used would be C. + +You can use regular expressions to match a large number of values with just one +configuration option. To select all "extended" I values, you could use the +following statement: + + Value "/^TcpExt:/" + +Whether only matched values are selected or all matched values are ignored +depends on the B. By default, only matched values are selected. +If no value is configured at all, all values will be selected. + +=item B B|B + +If set to B, inverts the selection made by B, i.Ee. all +matching values will be ignored. + +=back + =head2 Plugin C The C plugin uses the RRDTool accelerator daemon, L, @@ -2439,9 +2723,109 @@ debugging support. =back +=head2 Plugin C + +The C
provides generic means to parse tabular data and dispatch +user specified values. Values are selected based on column numbers. For +example, this plugin may be used to get values from the Linux L +filesystem or CSV (comma separated values) files. + + +
+ Instance "slabinfo" + Separator " " + + Type gauge + InstancePrefix "active_objs" + InstancesFrom 0 + ValuesFrom 1 + + + Type gauge + InstancePrefix "objperslab" + InstancesFrom 0 + ValuesFrom 4 + +
+
+ +The configuration consists of one or more B blocks, each of which +configures one file to parse. Within each B
block, there are one or +more B blocks, which configure which data to select and how to +interpret it. + +The following options are available inside a B
block: + +=over 4 + +=item B I + +If specified, I is used as the plugin instance. So, in the above +example, the plugin name C would be used. If omitted, the +filename of the table is used instead, with all special characters replaced +with an underscore (C<_>). + +=item B I + +Any character of I is interpreted as a delimiter between the different +columns of the table. A sequence of two or more contiguous delimiters in the +table is considered to be a single delimiter, i.Ee. there cannot be any +empty columns. The plugin uses the L function to parse the lines +of a table - see its documentation for more details. This option is mandatory. + +A horizontal tab, newline and carriage return may be specified by C<\\t>, +C<\\n> and C<\\r> respectively. Please note that the double backslashes are +required because of collectd's config parsing. + +=back + +The following options are available inside a B block: + +=over 4 + +=item B I + +Sets the type used to dispatch the values to the daemon. Detailed information +about types and their configuration can be found in L. This +option is mandatory. + +=item B I + +If specified, prepend I to the type instance. If omitted, only the +B option is considered for the type instance. + +=item B I [I ...] + +If specified, the content of the given columns (identified by the column +number starting at zero) will be used to create the type instance for each +row. Multiple values (and the instance prefix) will be joined together with +dashes (I<->) as separation character. If omitted, only the B +option is considered for the type instance. + +The plugin itself does not check whether or not all built instances are +different. It’s your responsibility to assure that each is unique. This is +especially true, if you do not specify B: B have to make +sure that the table only contains one row. + +If neither B nor B is given, the type instance +will be empty. + +=item B I [I ...] + +Specifies the columns (identified by the column numbers starting at zero) +whose content is used as the actual data for the data sets that are dispatched +to the daemon. How many such columns you need is determined by the B +setting above. If you specify too many or not enough columns, the plugin will +complain about that and no data will be submitted to the daemon. The plugin +uses L and L to parse counter and gauge values +respectively, so anything supported by those functions is supported by the +plugin as well. This option is mandatory. + +=back + =head2 Plugin C -The C plugins follows logfiles, just like L does, parses +The C follows logfiles, just like L does, parses each line and dispatches found values. What is matched can be configured by the user using (extended) regular expressions, as described in L. @@ -2578,6 +2962,37 @@ will be collected. =back +=head2 Plugin C + +The I plugin connects to a device of "The Energy Detective", a device to +measure power consumption. These devices are usually connected to a serial +(RS232) or USB port. The plugin opens a configured device and tries to read the +current energy readings. For more information on TED, visit +L. + +Available configuration options: + +=over 4 + +=item B I + +Path to the device on which TED is connected. collectd will need read and write +permissions on that file. + +Default: B + +=item B I + +Apparently reading from TED is not that reliable. You can therefore configure a +number of retries here. You only configure the I here, to if you +specify zero, one reading will be performed (but no retries if that fails); if +you specify three, a maximum of four readings are performed. Negative values +are illegal. + +Default: B<0> + +=back + =head2 Plugin C The C counts the number of currently established TCP diff --git a/src/common.c b/src/common.c index 1af2f144..d42453e4 100644 --- a/src/common.c +++ b/src/common.c @@ -253,7 +253,7 @@ int strsplit (char *string, char **fields, size_t size) break; } - return (i); + return ((int) i); } int strjoin (char *dst, size_t dst_len, @@ -317,6 +317,40 @@ int strsubstitute (char *str, char c_from, char c_to) return (ret); } /* int strsubstitute */ +int strunescape (char *buf, size_t buf_len) +{ + size_t i; + + for (i = 0; (i < buf_len) && (buf[i] != '\0'); ++i) + { + if (buf[i] != '\\') + continue; + + if ((i >= buf_len) || (buf[i + 1] == '\0')) { + ERROR ("string unescape: backslash found at end of string."); + return (-1); + } + + switch (buf[i + 1]) { + case 't': + buf[i] = '\t'; + break; + case 'n': + buf[i] = '\n'; + break; + case 'r': + buf[i] = '\r'; + break; + default: + buf[i] = buf[i + 1]; + break; + } + + memmove (buf + i + 1, buf + i + 2, buf_len - i - 2); + } + return (0); +} /* int strunescape */ + int escape_slashes (char *buf, int buf_len) { int i; @@ -349,6 +383,19 @@ int escape_slashes (char *buf, int buf_len) return (0); } /* int escape_slashes */ +void replace_special (char *buffer, size_t buffer_size) +{ + size_t i; + + for (i = 0; i < buffer_size; i++) + { + if (buffer[i] == 0) + return; + if ((!isalnum ((int) buffer[i])) && (buffer[i] != '-')) + buffer[i] = '_'; + } +} /* void replace_special */ + int timeval_cmp (struct timeval tv0, struct timeval tv1, struct timeval *delta) { struct timeval *larger; @@ -790,6 +837,30 @@ int parse_identifier (char *str, char **ret_host, return (0); } /* int parse_identifier */ +int parse_value (const char *value, value_t *ret_value, const data_source_t ds) +{ + char *endptr = NULL; + + if (DS_TYPE_COUNTER == ds.type) + ret_value->counter = (counter_t)strtoll (value, &endptr, 0); + else if (DS_TYPE_GAUGE == ds.type) + ret_value->gauge = (gauge_t)strtod (value, &endptr); + else { + ERROR ("parse_value: Invalid data source \"%s\" " + "(type = %i).", ds.name, ds.type); + return -1; + } + + if (value == endptr) { + ERROR ("parse_value: Failed to parse string as number: %s.", value); + return -1; + } + else if ((NULL != endptr) && ('\0' != *endptr)) + WARNING ("parse_value: Ignoring trailing garbage after number: %s.", + endptr); + return 0; +} /* int parse_value */ + int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds) { int i; @@ -816,12 +887,10 @@ int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds) } else { - if (strcmp ("U", ptr) == 0) + if ((strcmp ("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE)) vl->values[i].gauge = NAN; - else if (ds->ds[i].type == DS_TYPE_COUNTER) - vl->values[i].counter = atoll (ptr); - else if (ds->ds[i].type == DS_TYPE_GAUGE) - vl->values[i].gauge = atof (ptr); + else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i])) + return -1; } i++; diff --git a/src/common.h b/src/common.h index 85db3adb..d0cc4e82 100644 --- a/src/common.h +++ b/src/common.h @@ -98,7 +98,7 @@ ssize_t swrite (int fd, const void *buf, size_t count); * * DESCRIPTION * Splits a string into parts and stores pointers to the parts in `fields'. - * The characters split at are ` ' (space) and "\t" (tab). + * The characters split at are: " ", "\t", "\r", and "\n". * * PARAMETERS * `string' String to split. This string will be modified. `fields' will @@ -158,8 +158,48 @@ int strjoin (char *dst, size_t dst_len, char **fields, size_t fields_num, const */ int escape_slashes (char *buf, int buf_len); +/* + * NAME + * replace_special + * + * DESCRIPTION + * Replaces any special characters (anything that's not alpha-numeric or a + * dash) with an underscore. + * + * E.g. "foo$bar&" would become "foo_bar_". + * + * PARAMETERS + * `buffer' String to be handled. + * `buffer_size' Length of the string. The function returns after + * encountering a null-byte or reading this many bytes. + */ +void replace_special (char *buffer, size_t buffer_size); + int strsubstitute (char *str, char c_from, char c_to); +/* + * NAME + * strunescape + * + * DESCRIPTION + * Replaces any escaped characters in a string with the appropriate special + * characters. The following escaped characters are recognized: + * + * \t -> + * \n -> + * \r -> + * + * For all other escacped characters only the backslash will be removed. + * + * PARAMETERS + * `buf' String to be unescaped. + * `buf_len' Length of the string, including the terminating null-byte. + * + * RETURN VALUE + * Returns zero upon success, a value less than zero else. + */ +int strunescape (char *buf, size_t buf_len); + /* * NAME * timeval_cmp @@ -182,6 +222,13 @@ int timeval_cmp (struct timeval tv0, struct timeval tv1, struct timeval *delta); (tv).tv_usec = (tv).tv_usec % 1000000; \ } while (0) +/* make sure tv_sec stores less than a second */ +#define NORMALIZE_TIMESPEC(tv) \ + do { \ + (tv).tv_sec += (tv).tv_nsec / 1000000000; \ + (tv).tv_nsec = (tv).tv_nsec % 1000000000; \ + } while (0) + int check_create_dir (const char *file_orig); #ifdef HAVE_LIBKSTAT @@ -213,6 +260,7 @@ int format_name (char *ret, int ret_len, int parse_identifier (char *str, char **ret_host, char **ret_plugin, char **ret_plugin_instance, char **ret_type, char **ret_type_instance); +int parse_value (const char *value, value_t *ret_value, const data_source_t ds); int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds); #if !HAVE_GETPWNAM_R diff --git a/src/configfile.c b/src/configfile.c index c929d009..0e28e92f 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -172,7 +172,7 @@ static int cf_dispatch (const char *type, const char *orig_key, free (key); free (value); - DEBUG ("return (%i)", ret); + DEBUG ("cf_dispatch: return (%i)", ret); return (ret); } /* int cf_dispatch */ diff --git a/src/csv.c b/src/csv.c index 352557a8..d01f47de 100644 --- a/src/csv.c +++ b/src/csv.c @@ -249,7 +249,8 @@ static int csv_config (const char *key, const char *value) return (0); } /* int csv_config */ -static int csv_write (const data_set_t *ds, const value_list_t *vl) +static int csv_write (const data_set_t *ds, const value_list_t *vl, + user_data_t __attribute__((unused)) *user_data) { struct stat statbuf; char filename[512]; @@ -356,5 +357,5 @@ void module_register (void) { plugin_register_config ("csv", csv_config, config_keys, config_keys_num); - plugin_register_write ("csv", csv_write); + plugin_register_write ("csv", csv_write, /* user_data = */ NULL); } /* void module_register */ diff --git a/src/exec.c b/src/exec.c index c2d42ee0..82aeb29d 100644 --- a/src/exec.c +++ b/src/exec.c @@ -773,7 +773,8 @@ static int exec_read (void) /* {{{ */ return (0); } /* int exec_read }}} */ -static int exec_notification (const notification_t *n) +static int exec_notification (const notification_t *n, + user_data_t __attribute__((unused)) *user_data) { program_list_t *pl; program_list_and_notification_t *pln; @@ -813,7 +814,7 @@ static int exec_notification (const notification_t *n) } /* for (pl) */ return (0); -} /* int exec_notification */ +} /* }}} int exec_notification */ static int exec_shutdown (void) /* {{{ */ { @@ -846,7 +847,8 @@ void module_register (void) plugin_register_complex_config ("exec", exec_config); plugin_register_init ("exec", exec_init); plugin_register_read ("exec", exec_read); - plugin_register_notification ("exec", exec_notification); + plugin_register_notification ("exec", exec_notification, + /* user_data = */ NULL); plugin_register_shutdown ("exec", exec_shutdown); } /* void module_register */ diff --git a/src/gmond.c b/src/gmond.c new file mode 100644 index 00000000..93bbc95d --- /dev/null +++ b/src/gmond.c @@ -0,0 +1,1100 @@ +/** + * collectd - src/gmond.c + * Copyright (C) 2005-2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" +#include "plugin.h" +#include "common.h" +#include "configfile.h" +#include "utils_avltree.h" + +#if HAVE_PTHREAD_H +# include +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif +#if HAVE_NETDB_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +#if HAVE_ARPA_INET_H +# include +#endif +#if HAVE_POLL_H +# include +#endif + +#include + +#ifndef IPV6_ADD_MEMBERSHIP +# ifdef IPV6_JOIN_GROUP +# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +# else +# error "Neither IP_ADD_MEMBERSHIP nor IPV6_JOIN_GROUP is defined" +# endif +#endif /* !IP_ADD_MEMBERSHIP */ + +#ifdef GANGLIA_MAX_MESSAGE_LEN +# define BUFF_SIZE GANGLIA_MAX_MESSAGE_LEN +#else +# define BUFF_SIZE 1400 +#endif + +struct socket_entry_s +{ + int fd; + struct sockaddr_storage addr; + socklen_t addrlen; +}; +typedef struct socket_entry_s socket_entry_t; + +struct staging_entry_s +{ + char key[2 * DATA_MAX_NAME_LEN]; + value_list_t vl; + int flags; +}; +typedef struct staging_entry_s staging_entry_t; + +struct metric_map_s +{ + char *ganglia_name; + char *type; + char *type_instance; + char *ds_name; + int ds_type; + int ds_index; +}; +typedef struct metric_map_s metric_map_t; + +#define MC_RECEIVE_GROUP_DEFAULT "239.2.11.71" +static char *mc_receive_group = NULL; +#define MC_RECEIVE_PORT_DEFAULT "8649" +static char *mc_receive_port = NULL; + +static struct pollfd *mc_receive_sockets = NULL; +static size_t mc_receive_sockets_num = 0; + +static socket_entry_t *mc_send_sockets = NULL; +static size_t mc_send_sockets_num = 0; +static pthread_mutex_t mc_send_sockets_lock = PTHREAD_MUTEX_INITIALIZER; + +static int mc_receive_thread_loop = 0; +static int mc_receive_thread_running = 0; +static pthread_t mc_receive_thread_id; + +static metric_map_t metric_map_default[] = +{ /*---------------+-------------+-----------+-------------+------+-----* + * ganglia_name ! type ! type_inst ! data_source ! type ! idx * + *---------------+-------------+-----------+-------------+------+-----*/ + { "load_one", "load", "", "shortterm", -1, -1 }, + { "load_five", "load", "", "midterm", -1, -1 }, + { "load_fifteen", "load", "", "longterm", -1, -1 }, + { "cpu_user", "cpu", "user", "value", -1, -1 }, + { "cpu_system", "cpu", "system", "value", -1, -1 }, + { "cpu_idle", "cpu", "idle", "value", -1, -1 }, + { "cpu_nice", "cpu", "nice", "value", -1, -1 }, + { "cpu_wio", "cpu", "wait", "value", -1, -1 }, + { "mem_free", "memory", "free", "value", -1, -1 }, + { "mem_shared", "memory", "shared", "value", -1, -1 }, + { "mem_buffers", "memory", "buffered", "value", -1, -1 }, + { "mem_cached", "memory", "cached", "value", -1, -1 }, + { "mem_total", "memory", "total", "value", -1, -1 }, + { "bytes_in", "if_octets", "", "rx", -1, -1 }, + { "bytes_out", "if_octets", "", "tx", -1, -1 }, + { "pkts_in", "if_packets", "", "rx", -1, -1 }, + { "pkts_out", "if_packets", "", "tx", -1, -1 } +}; +static size_t metric_map_len_default = STATIC_ARRAY_SIZE (metric_map_default); + +static metric_map_t *metric_map = NULL; +static size_t metric_map_len = 0; + +static c_avl_tree_t *staging_tree; +static pthread_mutex_t staging_lock = PTHREAD_MUTEX_INITIALIZER; + +static metric_map_t *metric_lookup (const char *key) /* {{{ */ +{ + metric_map_t *map; + size_t map_len; + size_t i; + + /* Search the user-supplied table first.. */ + map = metric_map; + map_len = metric_map_len; + for (i = 0; i < map_len; i++) + if (strcmp (map[i].ganglia_name, key) == 0) + break; + + /* .. and fall back to the built-in table if nothing is found. */ + if (i >= map_len) + { + map = metric_map_default; + map_len = metric_map_len_default; + + for (i = 0; i < map_len; i++) + if (strcmp (map[i].ganglia_name, key) == 0) + break; + } + + if (i >= map_len) + return (NULL); + + /* Look up the DS type and ds_index. */ + if ((map[i].ds_type < 0) || (map[i].ds_index < 0)) /* {{{ */ + { + const data_set_t *ds; + + ds = plugin_get_ds (map[i].type); + if (ds == NULL) + { + WARNING ("gmond plugin: Type not defined: %s", map[i].type); + return (NULL); + } + + if ((map[i].ds_name == NULL) && (ds->ds_num != 1)) + { + WARNING ("gmond plugin: No data source name defined for metric %s, " + "but type %s has more than one data source.", + map[i].ganglia_name, map[i].type); + return (NULL); + } + + if (map[i].ds_name == NULL) + { + map[i].ds_index = 0; + } + else + { + int j; + + for (j = 0; j < ds->ds_num; j++) + if (strcasecmp (ds->ds[j].name, map[i].ds_name) == 0) + break; + + if (j >= ds->ds_num) + { + WARNING ("gmond plugin: There is no data source " + "named `%s' in type `%s'.", + map[i].ds_name, ds->type); + return (NULL); + } + map[i].ds_index = j; + } + + map[i].ds_type = ds->ds[map[i].ds_index].type; + } /* }}} if ((map[i].ds_type < 0) || (map[i].ds_index < 0)) */ + + return (map + i); +} /* }}} metric_map_t *metric_lookup */ + +static int create_sockets (socket_entry_t **ret_sockets, /* {{{ */ + size_t *ret_sockets_num, + const char *node, const char *service, int listen) +{ + struct addrinfo ai_hints; + struct addrinfo *ai_list; + struct addrinfo *ai_ptr; + int ai_return; + + socket_entry_t *sockets; + size_t sockets_num; + + int status; + + sockets = *ret_sockets; + sockets_num = *ret_sockets_num; + + memset (&ai_hints, 0, sizeof (ai_hints)); + ai_hints.ai_flags = 0; +#ifdef AI_PASSIVE + ai_hints.ai_flags |= AI_PASSIVE; +#endif +#ifdef AI_ADDRCONFIG + ai_hints.ai_flags |= AI_ADDRCONFIG; +#endif + ai_hints.ai_family = AF_UNSPEC; + ai_hints.ai_socktype = SOCK_DGRAM; + ai_hints.ai_protocol = IPPROTO_UDP; + + ai_return = getaddrinfo (node, service, &ai_hints, &ai_list); + if (ai_return != 0) + { + char errbuf[1024]; + ERROR ("gmond plugin: getaddrinfo (%s, %s) failed: %s", + (node == NULL) ? "(null)" : node, + (service == NULL) ? "(null)" : service, + (ai_return == EAI_SYSTEM) + ? sstrerror (errno, errbuf, sizeof (errbuf)) + : gai_strerror (ai_return)); + return (-1); + } + + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) /* {{{ */ + { + socket_entry_t *tmp; + + tmp = realloc (sockets, (sockets_num + 1) * sizeof (*sockets)); + if (tmp == NULL) + { + ERROR ("gmond plugin: realloc failed."); + continue; + } + sockets = tmp; + + sockets[sockets_num].fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, + ai_ptr->ai_protocol); + if (sockets[sockets_num].fd < 0) + { + char errbuf[1024]; + ERROR ("gmond plugin: socket failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + continue; + } + + assert (sizeof (sockets[sockets_num].addr) >= ai_ptr->ai_addrlen); + memcpy (&sockets[sockets_num].addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen); + sockets[sockets_num].addrlen = ai_ptr->ai_addrlen; + + /* Sending socket: Open only one socket and don't bind it. */ + if (listen == 0) + { + sockets_num++; + break; + } + else + { + int yes = 1; + + setsockopt (sockets[sockets_num].fd, SOL_SOCKET, SO_REUSEADDR, + (void *) &yes, sizeof (yes)); + } + + status = bind (sockets[sockets_num].fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen); + if (status != 0) + { + char errbuf[1024]; + ERROR ("gmond plugin: bind failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + close (sockets[sockets_num].fd); + continue; + } + + if (ai_ptr->ai_family == AF_INET) + { + struct sockaddr_in *addr; + struct ip_mreq mreq; + int loop; + + addr = (struct sockaddr_in *) ai_ptr->ai_addr; + + if (!IN_MULTICAST (ntohl (addr->sin_addr.s_addr))) + { + sockets_num++; + continue; + } + + loop = 1; + setsockopt (sockets[sockets_num].fd, IPPROTO_IP, IP_MULTICAST_LOOP, + (void *) &loop, sizeof (loop)); + + memset (&mreq, 0, sizeof (mreq)); + mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr; + mreq.imr_interface.s_addr = htonl (INADDR_ANY); + setsockopt (sockets[sockets_num].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (void *) &mreq, sizeof (mreq)); + } /* if (ai_ptr->ai_family == AF_INET) */ + else if (ai_ptr->ai_family == AF_INET6) + { + struct sockaddr_in6 *addr; + struct ipv6_mreq mreq; + int loop; + + addr = (struct sockaddr_in6 *) ai_ptr->ai_addr; + + if (!IN6_IS_ADDR_MULTICAST (&addr->sin6_addr)) + { + sockets_num++; + continue; + } + + loop = 1; + setsockopt (sockets[sockets_num].fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, + (void *) &loop, sizeof (loop)); + + memset (&mreq, 0, sizeof (mreq)); + memcpy (&mreq.ipv6mr_multiaddr, + &addr->sin6_addr, sizeof (addr->sin6_addr)); + mreq.ipv6mr_interface = 0; /* any */ + setsockopt (sockets[sockets_num].fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, + (void *) &mreq, sizeof (mreq)); + } /* if (ai_ptr->ai_family == AF_INET6) */ + + sockets_num++; + } /* }}} for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) */ + + freeaddrinfo (ai_list); + + if ((*ret_sockets_num) >= sockets_num) + return (-1); + + *ret_sockets = sockets; + *ret_sockets_num = sockets_num; + return (0); +} /* }}} int create_sockets */ + +static int request_meta_data (const char *host, const char *name) /* {{{ */ +{ + Ganglia_metadata_msg msg; + char buffer[BUFF_SIZE]; + unsigned int buffer_size; + XDR xdr; + size_t i; + + memset (&msg, 0, sizeof (msg)); + + msg.id = gmetadata_request; + msg.Ganglia_metadata_msg_u.grequest.metric_id.host = strdup (host); + msg.Ganglia_metadata_msg_u.grequest.metric_id.name = strdup (name); + + if ((msg.Ganglia_metadata_msg_u.grequest.metric_id.host == NULL) + || (msg.Ganglia_metadata_msg_u.grequest.metric_id.name == NULL)) + { + sfree (msg.Ganglia_metadata_msg_u.grequest.metric_id.host); + sfree (msg.Ganglia_metadata_msg_u.grequest.metric_id.name); + return (-1); + } + + memset (buffer, 0, sizeof (buffer)); + xdrmem_create (&xdr, buffer, sizeof (buffer), XDR_ENCODE); + + if (!xdr_Ganglia_metadata_msg (&xdr, &msg)) + { + sfree (msg.Ganglia_metadata_msg_u.grequest.metric_id.host); + sfree (msg.Ganglia_metadata_msg_u.grequest.metric_id.name); + return (-1); + } + + buffer_size = xdr_getpos (&xdr); + + DEBUG ("gmond plugin: Requesting meta data for %s/%s.", + host, name); + + pthread_mutex_lock (&mc_send_sockets_lock); + for (i = 0; i < mc_send_sockets_num; i++) + sendto (mc_send_sockets[i].fd, buffer, (size_t) buffer_size, + /* flags = */ 0, + (struct sockaddr *) &mc_send_sockets[i].addr, + mc_send_sockets[i].addrlen); + pthread_mutex_unlock (&mc_send_sockets_lock); + + sfree (msg.Ganglia_metadata_msg_u.grequest.metric_id.host); + sfree (msg.Ganglia_metadata_msg_u.grequest.metric_id.name); + return (0); +} /* }}} int request_meta_data */ + +static staging_entry_t *staging_entry_get (const char *host, /* {{{ */ + const char *name, + const char *type, const char *type_instance, + int values_len) +{ + char key[2 * DATA_MAX_NAME_LEN]; + staging_entry_t *se; + int status; + + if (staging_tree == NULL) + return (NULL); + + ssnprintf (key, sizeof (key), "%s/%s/%s", host, type, + (type_instance != NULL) ? type_instance : ""); + + se = NULL; + status = c_avl_get (staging_tree, key, (void *) &se); + if (status == 0) + return (se); + + /* insert new entry */ + se = (staging_entry_t *) malloc (sizeof (*se)); + if (se == NULL) + return (NULL); + memset (se, 0, sizeof (*se)); + + sstrncpy (se->key, key, sizeof (se->key)); + se->flags = 0; + + se->vl.values = (value_t *) calloc (values_len, sizeof (*se->vl.values)); + if (se->vl.values == NULL) + { + sfree (se); + return (NULL); + } + se->vl.values_len = values_len; + + se->vl.time = 0; + se->vl.interval = 0; + sstrncpy (se->vl.host, host, sizeof (se->vl.host)); + sstrncpy (se->vl.plugin, "gmond", sizeof (se->vl.plugin)); + sstrncpy (se->vl.type, type, sizeof (se->vl.type)); + if (type_instance != NULL) + sstrncpy (se->vl.type_instance, type_instance, + sizeof (se->vl.type_instance)); + + status = c_avl_insert (staging_tree, se->key, se); + if (status != 0) + { + ERROR ("gmond plugin: c_avl_insert failed."); + sfree (se->vl.values); + sfree (se); + return (NULL); + } + + return (se); +} /* }}} staging_entry_t *staging_entry_get */ + +static int staging_entry_submit (const char *host, const char *name, /* {{{ */ + staging_entry_t *se) +{ + value_list_t vl; + value_t values[se->vl.values_len]; + + if (se->vl.interval == 0) + { + /* No meta data has been received for this metric yet. */ + se->flags = 0; + pthread_mutex_unlock (&staging_lock); + request_meta_data (host, name); + return (0); + } + + se->flags = 0; + + memcpy (values, se->vl.values, sizeof (values)); + memcpy (&vl, &se->vl, sizeof (vl)); + + /* Unlock before calling `plugin_dispatch_values'.. */ + pthread_mutex_unlock (&staging_lock); + + vl.values = values; + + plugin_dispatch_values (&vl); + + return (0); +} /* }}} int staging_entry_submit */ + +static int staging_entry_update (const char *host, const char *name, /* {{{ */ + const char *type, const char *type_instance, + int ds_index, int ds_type, value_t value) +{ + const data_set_t *ds; + staging_entry_t *se; + + ds = plugin_get_ds (type); + if (ds == NULL) + { + ERROR ("gmond plugin: Looking up type %s failed.", type); + return (-1); + } + + if (ds->ds_num <= ds_index) + { + ERROR ("gmond plugin: Invalid index %i: %s has only %i data source(s).", + ds_index, ds->type, ds->ds_num); + return (-1); + } + + pthread_mutex_lock (&staging_lock); + + se = staging_entry_get (host, name, type, type_instance, ds->ds_num); + if (se == NULL) + { + pthread_mutex_unlock (&staging_lock); + ERROR ("gmond plugin: staging_entry_get failed."); + return (-1); + } + if (se->vl.values_len != ds->ds_num) + { + pthread_mutex_unlock (&staging_lock); + return (-1); + } + + if (ds_type == DS_TYPE_COUNTER) + se->vl.values[ds_index].counter += value.counter; + else if (ds_type == DS_TYPE_GAUGE) + se->vl.values[ds_index].gauge = value.gauge; + se->flags |= (0x01 << ds_index); + + /* Check if all values have been set and submit if so. */ + if (se->flags == ((0x01 << se->vl.values_len) - 1)) + { + /* `staging_lock' is unlocked in `staging_entry_submit'. */ + staging_entry_submit (host, name, se); + } + else + { + pthread_mutex_unlock (&staging_lock); + } + + return (0); +} /* }}} int staging_entry_update */ + +static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */ +{ + const char *host; + const char *name; + metric_map_t *map; + + value_t value_counter; + value_t value_gauge; + + /* Fill in `host', `name', `value_counter', and `value_gauge' according to + * the value type, or return with an error. */ + switch (msg->id) /* {{{ */ + { + case gmetric_uint: + { + Ganglia_gmetric_uint msg_uint; + + msg_uint = msg->Ganglia_value_msg_u.gu_int; + + host = msg_uint.metric_id.host; + name = msg_uint.metric_id.name; + value_counter.counter = (counter_t) msg_uint.ui; + value_gauge.gauge = (gauge_t) msg_uint.ui; + break; + } + + case gmetric_string: + { + Ganglia_gmetric_string msg_string; + char *endptr; + + msg_string = msg->Ganglia_value_msg_u.gstr; + + host = msg_string.metric_id.host; + name = msg_string.metric_id.name; + + endptr = NULL; + errno = 0; + value_counter.counter = (counter_t) strtoll (msg_string.str, + &endptr, /* base = */ 0); + if ((endptr == msg_string.str) || (errno != 0)) + value_counter.counter = -1; + + endptr = NULL; + errno = 0; + value_gauge.gauge = (gauge_t) strtod (msg_string.str, &endptr); + if ((endptr == msg_string.str) || (errno != 0)) + value_gauge.gauge = NAN; + + break; + } + + case gmetric_float: + { + Ganglia_gmetric_float msg_float; + + msg_float = msg->Ganglia_value_msg_u.gf; + + host = msg_float.metric_id.host; + name = msg_float.metric_id.name; + value_counter.counter = (counter_t) msg_float.f; + value_gauge.gauge = (gauge_t) msg_float.f; + break; + } + + case gmetric_double: + { + Ganglia_gmetric_double msg_double; + + msg_double = msg->Ganglia_value_msg_u.gd; + + host = msg_double.metric_id.host; + name = msg_double.metric_id.name; + value_counter.counter = (counter_t) msg_double.d; + value_gauge.gauge = (gauge_t) msg_double.d; + break; + } + default: + DEBUG ("gmond plugin: Value type not handled: %i", msg->id); + return (-1); + } /* }}} switch (msg->id) */ + + assert (host != NULL); + assert (name != NULL); + + map = metric_lookup (name); + if (map != NULL) + return (staging_entry_update (host, name, + map->type, map->type_instance, + map->ds_index, map->ds_type, + (map->ds_type == DS_TYPE_COUNTER) ? value_counter : value_gauge)); + + DEBUG ("gmond plugin: Cannot find a translation for %s.", name); + return (-1); +} /* }}} int mc_handle_value_msg */ + +static int mc_handle_metadata_msg (Ganglia_metadata_msg *msg) /* {{{ */ +{ + switch (msg->id) + { + case gmetadata_full: + { + Ganglia_metadatadef msg_meta; + staging_entry_t *se; + const data_set_t *ds; + metric_map_t *map; + + msg_meta = msg->Ganglia_metadata_msg_u.gfull; + + if (msg_meta.metric.tmax <= 0) + return (-1); + + map = metric_lookup (msg_meta.metric_id.name); + if (map == NULL) + { + DEBUG ("gmond plugin: Not handling meta data %s.", + msg_meta.metric_id.name); + return (0); + } + + ds = plugin_get_ds (map->type); + if (ds == NULL) + { + WARNING ("gmond plugin: Could not find data set %s.", map->type); + return (-1); + } + + DEBUG ("gmond plugin: Received meta data for %s/%s.", + msg_meta.metric_id.host, msg_meta.metric_id.name); + + pthread_mutex_lock (&staging_lock); + se = staging_entry_get (msg_meta.metric_id.host, + msg_meta.metric_id.name, + map->type, map->type_instance, + ds->ds_num); + if (se != NULL) + se->vl.interval = (int) msg_meta.metric.tmax; + pthread_mutex_unlock (&staging_lock); + + if (se == NULL) + { + ERROR ("gmond plugin: staging_entry_get failed."); + return (-1); + } + + break; + } + + default: + { + return (-1); + } + } + + return (0); +} /* }}} int mc_handle_metadata_msg */ + +static int mc_handle_metric (void *buffer, size_t buffer_size) /* {{{ */ +{ + XDR xdr; + Ganglia_msg_formats format; + + xdrmem_create (&xdr, buffer, buffer_size, XDR_DECODE); + + xdr_Ganglia_msg_formats (&xdr, &format); + xdr_setpos (&xdr, 0); + + switch (format) + { + case gmetric_ushort: + case gmetric_short: + case gmetric_int: + case gmetric_uint: + case gmetric_string: + case gmetric_float: + case gmetric_double: + { + Ganglia_value_msg msg; + + memset (&msg, 0, sizeof (msg)); + if (xdr_Ganglia_value_msg (&xdr, &msg)) + mc_handle_value_msg (&msg); + break; + } + + case gmetadata_full: + case gmetadata_request: + { + Ganglia_metadata_msg msg; + memset (&msg, 0, sizeof (msg)); + if (xdr_Ganglia_metadata_msg (&xdr, &msg)) + mc_handle_metadata_msg (&msg); + break; + } + + default: + DEBUG ("gmond plugin: Unknown format: %i", format); + return (-1); + } /* switch (format) */ + + + return (0); +} /* }}} int mc_handle_metric */ + +static int mc_handle_socket (struct pollfd *p) /* {{{ */ +{ + char buffer[BUFF_SIZE]; + ssize_t buffer_size; + + if ((p->revents & (POLLIN | POLLPRI)) == 0) + { + p->revents = 0; + return (-1); + } + + buffer_size = recv (p->fd, buffer, sizeof (buffer), /* flags = */ 0); + if (buffer_size <= 0) + { + char errbuf[1024]; + ERROR ("gmond plugin: recv failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + p->revents = 0; + return (-1); + } + + mc_handle_metric (buffer, (size_t) buffer_size); + return (0); +} /* }}} int mc_handle_socket */ + +static void *mc_receive_thread (void *arg) /* {{{ */ +{ + socket_entry_t *mc_receive_socket_entries; + int status; + size_t i; + + mc_receive_socket_entries = NULL; + status = create_sockets (&mc_receive_socket_entries, &mc_receive_sockets_num, + (mc_receive_group != NULL) ? mc_receive_group : MC_RECEIVE_GROUP_DEFAULT, + (mc_receive_port != NULL) ? mc_receive_port : MC_RECEIVE_PORT_DEFAULT, + /* listen = */ 1); + if (status != 0) + { + ERROR ("gmond plugin: create_sockets failed."); + return ((void *) -1); + } + + mc_receive_sockets = (struct pollfd *) calloc (mc_receive_sockets_num, + sizeof (*mc_receive_sockets)); + if (mc_receive_sockets == NULL) + { + ERROR ("gmond plugin: calloc failed."); + for (i = 0; i < mc_receive_sockets_num; i++) + close (mc_receive_socket_entries[i].fd); + free (mc_receive_socket_entries); + mc_receive_socket_entries = NULL; + mc_receive_sockets_num = 0; + return ((void *) -1); + } + + for (i = 0; i < mc_receive_sockets_num; i++) + { + mc_receive_sockets[i].fd = mc_receive_socket_entries[i].fd; + mc_receive_sockets[i].events = POLLIN | POLLPRI; + mc_receive_sockets[i].revents = 0; + } + + while (mc_receive_thread_loop != 0) + { + status = poll (mc_receive_sockets, mc_receive_sockets_num, -1); + if (status <= 0) + { + char errbuf[1024]; + if (errno == EINTR) + continue; + ERROR ("gmond plugin: poll failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + break; + } + + for (i = 0; i < mc_receive_sockets_num; i++) + { + if (mc_receive_sockets[i].revents != 0) + mc_handle_socket (mc_receive_sockets + i); + } + } /* while (mc_receive_thread_loop != 0) */ + + return ((void *) 0); +} /* }}} void *mc_receive_thread */ + +static int mc_receive_thread_start (void) /* {{{ */ +{ + int status; + + if (mc_receive_thread_running != 0) + return (-1); + + mc_receive_thread_loop = 1; + + status = pthread_create (&mc_receive_thread_id, /* attr = */ NULL, + mc_receive_thread, /* args = */ NULL); + if (status != 0) + { + ERROR ("gmond plugin: Starting receive thread failed."); + mc_receive_thread_loop = 0; + return (-1); + } + + mc_receive_thread_running = 1; + return (0); +} /* }}} int start_receive_thread */ + +static int mc_receive_thread_stop (void) /* {{{ */ +{ + if (mc_receive_thread_running == 0) + return (-1); + + mc_receive_thread_loop = 0; + + INFO ("gmond plugin: Stopping receive thread."); + pthread_kill (mc_receive_thread_id, SIGTERM); + pthread_join (mc_receive_thread_id, /* return value = */ NULL); + memset (&mc_receive_thread_id, 0, sizeof (mc_receive_thread_id)); + + mc_receive_thread_running = 0; + + return (0); +} /* }}} int mc_receive_thread_stop */ + +/* + * Config: + * + * + * MCReceiveFrom "239.2.11.71" "8649" + * + * Type "load" + * [TypeInstance "foo"] + * [DataSource "bar"] + * + * + */ +static int gmond_config_set_string (oconfig_item_t *ci, char **str) /* {{{ */ +{ + char *tmp; + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("gmond plugin: The `%s' option needs " + "exactly one string argument.", ci->key); + return (-1); + } + + tmp = strdup (ci->values[0].value.string); + if (tmp == NULL) + { + ERROR ("gmond plugin: strdup failed."); + return (-1); + } + + sfree (*str); + *str = tmp; + return (0); +} /* }}} int gmond_config_set_string */ + +static int gmond_config_add_metric (oconfig_item_t *ci) /* {{{ */ +{ + metric_map_t *map; + int i; + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("gmond plugin: `Metric' blocks need " + "exactly one string argument."); + return (-1); + } + + map = realloc (metric_map, (metric_map_len + 1) * sizeof (*metric_map)); + if (map == NULL) + { + ERROR ("gmond plugin: realloc failed."); + return (-1); + } + metric_map = map; + map = metric_map + metric_map_len; + + memset (map, 0, sizeof (*map)); + map->type = NULL; + map->type_instance = NULL; + map->ds_name = NULL; + map->ds_type = -1; + map->ds_index = -1; + + map->ganglia_name = strdup (ci->values[0].value.string); + if (map->ganglia_name == NULL) + { + ERROR ("gmond plugin: strdup failed."); + return (-1); + } + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + if (strcasecmp ("Type", child->key) == 0) + gmond_config_set_string (child, &map->type); + else if (strcasecmp ("TypeInstance", child->key) == 0) + gmond_config_set_string (child, &map->type_instance); + else if (strcasecmp ("DataSource", child->key) == 0) + gmond_config_set_string (child, &map->ds_name); + else + { + WARNING ("gmond plugin: Unknown configuration option `%s' ignored.", + child->key); + } + } + + if (map->type == NULL) + { + ERROR ("gmond plugin: No type is set for metric %s.", + map->ganglia_name); + sfree (map->ganglia_name); + sfree (map->type_instance); + return (-1); + } + + metric_map_len++; + return (0); +} /* }}} int gmond_config_add_metric */ + +static int gmond_config_set_address (oconfig_item_t *ci, /* {{{ */ + char **ret_addr, char **ret_port) +{ + char *addr; + char *port; + + if ((ci->values_num != 1) && (ci->values_num != 2)) + { + WARNING ("gmond plugin: The `%s' config option needs " + "one or two string arguments.", + ci->key); + return (-1); + } + if ((ci->values[0].type != OCONFIG_TYPE_STRING) + || ((ci->values_num == 2) + && (ci->values[1].type != OCONFIG_TYPE_STRING))) + { + WARNING ("gmond plugin: The `%s' config option needs " + "one or two string arguments.", + ci->key); + return (-1); + } + + addr = strdup (ci->values[0].value.string); + if (ci->values_num == 2) + port = strdup (ci->values[1].value.string); + else + port = NULL; + + if ((addr == NULL) || ((ci->values_num == 2) && (port == NULL))) + { + ERROR ("gmond plugin: strdup failed."); + sfree (addr); + sfree (port); + return (-1); + } + + sfree (*ret_addr); + sfree (*ret_port); + + *ret_addr = addr; + *ret_port = port; + + return (0); +} /* }}} int gmond_config_set_address */ + +static int gmond_config (oconfig_item_t *ci) /* {{{ */ +{ + int i; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + if (strcasecmp ("MCReceiveFrom", child->key) == 0) + gmond_config_set_address (child, &mc_receive_group, &mc_receive_port); + else if (strcasecmp ("Metric", child->key) == 0) + gmond_config_add_metric (child); + else + { + WARNING ("gmond plugin: Unknown configuration option `%s' ignored.", + child->key); + } + } + + return (0); +} /* }}} int gmond_config */ + +static int gmond_init (void) /* {{{ */ +{ + create_sockets (&mc_send_sockets, &mc_send_sockets_num, + (mc_receive_group != NULL) ? mc_receive_group : MC_RECEIVE_GROUP_DEFAULT, + (mc_receive_port != NULL) ? mc_receive_port : MC_RECEIVE_PORT_DEFAULT, + /* listen = */ 0); + + staging_tree = c_avl_create ((void *) strcmp); + if (staging_tree == NULL) + { + ERROR ("gmond plugin: c_avl_create failed."); + return (-1); + } + + mc_receive_thread_start (); + + return (0); +} /* }}} int gmond_init */ + +static int gmond_shutdown (void) /* {{{ */ +{ + size_t i; + + mc_receive_thread_stop (); + + pthread_mutex_lock (&mc_send_sockets_lock); + for (i = 0; i < mc_send_sockets_num; i++) + { + close (mc_send_sockets[i].fd); + mc_send_sockets[i].fd = -1; + } + sfree (mc_send_sockets); + mc_send_sockets_num = 0; + pthread_mutex_unlock (&mc_send_sockets_lock); + + + return (0); +} /* }}} int gmond_shutdown */ + +void module_register (void) +{ + plugin_register_complex_config ("gmond", gmond_config); + plugin_register_init ("gmond", gmond_init); + plugin_register_shutdown ("gmond", gmond_shutdown); +} + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/java.c b/src/java.c new file mode 100644 index 00000000..20523f90 --- /dev/null +++ b/src/java.c @@ -0,0 +1,3012 @@ +/** + * collectd - src/java.c + * Copyright (C) 2009 Florian octo Forster + * Copyright (C) 2008 Justo Alonso Achaques + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + * Justo Alonso Achaques + **/ + +#include "collectd.h" +#include "plugin.h" +#include "common.h" +#include "filter_chain.h" + +#include +#include + +#if !defined(JNI_VERSION_1_2) +# error "Need JNI 1.2 compatible interface!" +#endif + +/* + * Types + */ +struct cjni_jvm_env_s /* {{{ */ +{ + JNIEnv *jvm_env; + int reference_counter; +}; +typedef struct cjni_jvm_env_s cjni_jvm_env_t; +/* }}} */ + +struct java_plugin_class_s /* {{{ */ +{ + char *name; + jclass class; + jobject object; +}; +typedef struct java_plugin_class_s java_plugin_class_t; +/* }}} */ + +#define CB_TYPE_CONFIG 1 +#define CB_TYPE_INIT 2 +#define CB_TYPE_READ 3 +#define CB_TYPE_WRITE 4 +#define CB_TYPE_FLUSH 5 +#define CB_TYPE_SHUTDOWN 6 +#define CB_TYPE_LOG 7 +#define CB_TYPE_NOTIFICATION 8 +#define CB_TYPE_MATCH 9 +#define CB_TYPE_TARGET 10 +struct cjni_callback_info_s /* {{{ */ +{ + char *name; + int type; + jclass class; + jobject object; + jmethodID method; +}; +typedef struct cjni_callback_info_s cjni_callback_info_t; +/* }}} */ + +/* + * Global variables + */ +static JavaVM *jvm = NULL; +static pthread_key_t jvm_env_key; + +/* Configuration options for the JVM. */ +static char **jvm_argv = NULL; +static size_t jvm_argc = 0; + +/* List of class names to load */ +static java_plugin_class_t *java_classes_list = NULL; +static size_t java_classes_list_len; + +/* List of config, init, and shutdown callbacks. */ +static cjni_callback_info_t *java_callbacks = NULL; +static size_t java_callbacks_num = 0; +static pthread_mutex_t java_callbacks_lock = PTHREAD_MUTEX_INITIALIZER; + +/* + * Prototypes + * + * Mostly functions that are needed by the Java interface (``native'') + * functions. + */ +static void cjni_callback_info_destroy (void *arg); +static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, + jobject o_name, jobject o_callback, int type); +static int cjni_callback_register (JNIEnv *jvm_env, jobject o_name, + jobject o_callback, int type); +static int cjni_read (user_data_t *user_data); +static int cjni_write (const data_set_t *ds, const value_list_t *vl, + user_data_t *ud); +static int cjni_flush (int timeout, const char *identifier, user_data_t *ud); +static void cjni_log (int severity, const char *message, user_data_t *ud); +static int cjni_notification (const notification_t *n, user_data_t *ud); + +/* Create, destroy, and match/invoke functions, used by both, matches AND + * targets. */ +static int cjni_match_target_create (const oconfig_item_t *ci, void **user_data); +static int cjni_match_target_destroy (void **user_data); +static int cjni_match_target_invoke (const data_set_t *ds, value_list_t *vl, + notification_meta_t **meta, void **user_data); + +/* + * C to Java conversion functions + */ +static int ctoj_string (JNIEnv *jvm_env, /* {{{ */ + const char *string, + jclass class_ptr, jobject object_ptr, const char *method_name) +{ + jmethodID m_set; + jstring o_string; + + /* Create a java.lang.String */ + o_string = (*jvm_env)->NewStringUTF (jvm_env, + (string != NULL) ? string : ""); + if (o_string == NULL) + { + ERROR ("java plugin: ctoj_string: NewStringUTF failed."); + return (-1); + } + + /* Search for the `void setFoo (String s)' method. */ + m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr, + method_name, "(Ljava/lang/String;)V"); + if (m_set == NULL) + { + ERROR ("java plugin: ctoj_string: Cannot find method `void %s (String)'.", + method_name); + (*jvm_env)->DeleteLocalRef (jvm_env, o_string); + return (-1); + } + + /* Call the method. */ + (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, o_string); + + /* Decrease reference counter on the java.lang.String object. */ + (*jvm_env)->DeleteLocalRef (jvm_env, o_string); + + return (0); +} /* }}} int ctoj_string */ + +static int ctoj_int (JNIEnv *jvm_env, /* {{{ */ + jint value, + jclass class_ptr, jobject object_ptr, const char *method_name) +{ + jmethodID m_set; + + /* Search for the `void setFoo (int i)' method. */ + m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr, + method_name, "(I)V"); + if (m_set == NULL) + { + ERROR ("java plugin: ctoj_int: Cannot find method `void %s (int)'.", + method_name); + return (-1); + } + + (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value); + + return (0); +} /* }}} int ctoj_int */ + +static int ctoj_long (JNIEnv *jvm_env, /* {{{ */ + jlong value, + jclass class_ptr, jobject object_ptr, const char *method_name) +{ + jmethodID m_set; + + /* Search for the `void setFoo (long l)' method. */ + m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr, + method_name, "(J)V"); + if (m_set == NULL) + { + ERROR ("java plugin: ctoj_long: Cannot find method `void %s (long)'.", + method_name); + return (-1); + } + + (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value); + + return (0); +} /* }}} int ctoj_long */ + +static int ctoj_double (JNIEnv *jvm_env, /* {{{ */ + jdouble value, + jclass class_ptr, jobject object_ptr, const char *method_name) +{ + jmethodID m_set; + + /* Search for the `void setFoo (double d)' method. */ + m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr, + method_name, "(D)V"); + if (m_set == NULL) + { + ERROR ("java plugin: ctoj_double: Cannot find method `void %s (double)'.", + method_name); + return (-1); + } + + (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value); + + return (0); +} /* }}} int ctoj_double */ + +/* Convert a jlong to a java.lang.Number */ +static jobject ctoj_jlong_to_number (JNIEnv *jvm_env, jlong value) /* {{{ */ +{ + jclass c_long; + jmethodID m_long_constructor; + + /* Look up the java.lang.Long class */ + c_long = (*jvm_env)->FindClass (jvm_env, "java.lang.Long"); + if (c_long == NULL) + { + ERROR ("java plugin: ctoj_jlong_to_number: Looking up the " + "java.lang.Long class failed."); + return (NULL); + } + + m_long_constructor = (*jvm_env)->GetMethodID (jvm_env, + c_long, "", "(J)V"); + if (m_long_constructor == NULL) + { + ERROR ("java plugin: ctoj_jlong_to_number: Looking up the " + "`Long (long)' constructor failed."); + return (NULL); + } + + return ((*jvm_env)->NewObject (jvm_env, + c_long, m_long_constructor, value)); +} /* }}} jobject ctoj_jlong_to_number */ + +/* Convert a jdouble to a java.lang.Number */ +static jobject ctoj_jdouble_to_number (JNIEnv *jvm_env, jdouble value) /* {{{ */ +{ + jclass c_double; + jmethodID m_double_constructor; + + /* Look up the java.lang.Long class */ + c_double = (*jvm_env)->FindClass (jvm_env, "java.lang.Double"); + if (c_double == NULL) + { + ERROR ("java plugin: ctoj_jdouble_to_number: Looking up the " + "java.lang.Double class failed."); + return (NULL); + } + + m_double_constructor = (*jvm_env)->GetMethodID (jvm_env, + c_double, "", "(D)V"); + if (m_double_constructor == NULL) + { + ERROR ("java plugin: ctoj_jdouble_to_number: Looking up the " + "`Double (double)' constructor failed."); + return (NULL); + } + + return ((*jvm_env)->NewObject (jvm_env, + c_double, m_double_constructor, value)); +} /* }}} jobject ctoj_jdouble_to_number */ + +/* Convert a value_t to a java.lang.Number */ +static jobject ctoj_value_to_number (JNIEnv *jvm_env, /* {{{ */ + value_t value, int ds_type) +{ + if (ds_type == DS_TYPE_COUNTER) + return (ctoj_jlong_to_number (jvm_env, (jlong) value.counter)); + else if (ds_type == DS_TYPE_GAUGE) + return (ctoj_jdouble_to_number (jvm_env, (jdouble) value.gauge)); + else + return (NULL); +} /* }}} jobject ctoj_value_to_number */ + +/* Convert a data_source_t to a org/collectd/api/DataSource */ +static jobject ctoj_data_source (JNIEnv *jvm_env, /* {{{ */ + const data_source_t *dsrc) +{ + jclass c_datasource; + jmethodID m_datasource_constructor; + jobject o_datasource; + int status; + + /* Look up the DataSource class */ + c_datasource = (*jvm_env)->FindClass (jvm_env, + "org/collectd/api/DataSource"); + if (c_datasource == NULL) + { + ERROR ("java plugin: ctoj_data_source: " + "FindClass (org/collectd/api/DataSource) failed."); + return (NULL); + } + + /* Lookup the `ValueList ()' constructor. */ + m_datasource_constructor = (*jvm_env)->GetMethodID (jvm_env, c_datasource, + "", "()V"); + if (m_datasource_constructor == NULL) + { + ERROR ("java plugin: ctoj_data_source: Cannot find the " + "`DataSource ()' constructor."); + return (NULL); + } + + /* Create a new instance. */ + o_datasource = (*jvm_env)->NewObject (jvm_env, c_datasource, + m_datasource_constructor); + if (o_datasource == NULL) + { + ERROR ("java plugin: ctoj_data_source: " + "Creating a new DataSource instance failed."); + return (NULL); + } + + /* Set name via `void setName (String name)' */ + status = ctoj_string (jvm_env, dsrc->name, + c_datasource, o_datasource, "setName"); + if (status != 0) + { + ERROR ("java plugin: ctoj_data_source: " + "ctoj_string (setName) failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource); + return (NULL); + } + + /* Set type via `void setType (int type)' */ + status = ctoj_int (jvm_env, dsrc->type, + c_datasource, o_datasource, "setType"); + if (status != 0) + { + ERROR ("java plugin: ctoj_data_source: " + "ctoj_int (setType) failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource); + return (NULL); + } + + /* Set min via `void setMin (double min)' */ + status = ctoj_double (jvm_env, dsrc->min, + c_datasource, o_datasource, "setMin"); + if (status != 0) + { + ERROR ("java plugin: ctoj_data_source: " + "ctoj_double (setMin) failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource); + return (NULL); + } + + /* Set max via `void setMax (double max)' */ + status = ctoj_double (jvm_env, dsrc->max, + c_datasource, o_datasource, "setMax"); + if (status != 0) + { + ERROR ("java plugin: ctoj_data_source: " + "ctoj_double (setMax) failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource); + return (NULL); + } + + return (o_datasource); +} /* }}} jobject ctoj_data_source */ + +/* Convert a oconfig_value_t to a org/collectd/api/OConfigValue */ +static jobject ctoj_oconfig_value (JNIEnv *jvm_env, /* {{{ */ + oconfig_value_t ocvalue) +{ + jclass c_ocvalue; + jmethodID m_ocvalue_constructor; + jobject o_argument; + jobject o_ocvalue; + + m_ocvalue_constructor = NULL; + o_argument = NULL; + + c_ocvalue = (*jvm_env)->FindClass (jvm_env, + "org/collectd/api/OConfigValue"); + if (c_ocvalue == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: " + "FindClass (org/collectd/api/OConfigValue) failed."); + return (NULL); + } + + if (ocvalue.type == OCONFIG_TYPE_BOOLEAN) + { + jboolean tmp_boolean; + + tmp_boolean = (ocvalue.value.boolean == 0) ? JNI_FALSE : JNI_TRUE; + + m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue, + "", "(Z)V"); + if (m_ocvalue_constructor == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: Cannot find the " + "`OConfigValue (boolean)' constructor."); + return (NULL); + } + + return ((*jvm_env)->NewObject (jvm_env, + c_ocvalue, m_ocvalue_constructor, tmp_boolean)); + } /* if (ocvalue.type == OCONFIG_TYPE_BOOLEAN) */ + else if (ocvalue.type == OCONFIG_TYPE_STRING) + { + m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue, + "", "(Ljava/lang/String;)V"); + if (m_ocvalue_constructor == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: Cannot find the " + "`OConfigValue (String)' constructor."); + return (NULL); + } + + o_argument = (*jvm_env)->NewStringUTF (jvm_env, ocvalue.value.string); + if (o_argument == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: " + "Creating a String object failed."); + return (NULL); + } + } + else if (ocvalue.type == OCONFIG_TYPE_NUMBER) + { + m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue, + "", "(Ljava/lang/Number;)V"); + if (m_ocvalue_constructor == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: Cannot find the " + "`OConfigValue (Number)' constructor."); + return (NULL); + } + + o_argument = ctoj_jdouble_to_number (jvm_env, + (jdouble) ocvalue.value.number); + if (o_argument == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: " + "Creating a Number object failed."); + return (NULL); + } + } + else + { + return (NULL); + } + + assert (m_ocvalue_constructor != NULL); + assert (o_argument != NULL); + + o_ocvalue = (*jvm_env)->NewObject (jvm_env, + c_ocvalue, m_ocvalue_constructor, o_argument); + if (o_ocvalue == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: " + "Creating an OConfigValue object failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_argument); + return (NULL); + } + + (*jvm_env)->DeleteLocalRef (jvm_env, o_argument); + return (o_ocvalue); +} /* }}} jobject ctoj_oconfig_value */ + +/* Convert a oconfig_item_t to a org/collectd/api/OConfigItem */ +static jobject ctoj_oconfig_item (JNIEnv *jvm_env, /* {{{ */ + const oconfig_item_t *ci) +{ + jclass c_ocitem; + jmethodID m_ocitem_constructor; + jmethodID m_addvalue; + jmethodID m_addchild; + jobject o_key; + jobject o_ocitem; + int i; + + c_ocitem = (*jvm_env)->FindClass (jvm_env, "org/collectd/api/OConfigItem"); + if (c_ocitem == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: " + "FindClass (org/collectd/api/OConfigItem) failed."); + return (NULL); + } + + /* Get the required methods: m_ocitem_constructor, m_addvalue, and m_addchild + * {{{ */ + m_ocitem_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocitem, + "", "(Ljava/lang/String;)V"); + if (m_ocitem_constructor == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: Cannot find the " + "`OConfigItem (String)' constructor."); + return (NULL); + } + + m_addvalue = (*jvm_env)->GetMethodID (jvm_env, c_ocitem, + "addValue", "(Lorg/collectd/api/OConfigValue;)V"); + if (m_addvalue == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: Cannot find the " + "`addValue (OConfigValue)' method."); + return (NULL); + } + + m_addchild = (*jvm_env)->GetMethodID (jvm_env, c_ocitem, + "addChild", "(Lorg/collectd/api/OConfigItem;)V"); + if (m_addchild == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: Cannot find the " + "`addChild (OConfigItem)' method."); + return (NULL); + } + /* }}} */ + + /* Create a String object with the key. + * Needed for calling the constructor. */ + o_key = (*jvm_env)->NewStringUTF (jvm_env, ci->key); + if (o_key == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: " + "Creating String object failed."); + return (NULL); + } + + /* Create an OConfigItem object */ + o_ocitem = (*jvm_env)->NewObject (jvm_env, + c_ocitem, m_ocitem_constructor, o_key); + if (o_ocitem == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: " + "Creating an OConfigItem object failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_key); + return (NULL); + } + + /* We don't need the String object any longer.. */ + (*jvm_env)->DeleteLocalRef (jvm_env, o_key); + + /* Call OConfigItem.addValue for each value */ + for (i = 0; i < ci->values_num; i++) /* {{{ */ + { + jobject o_value; + + o_value = ctoj_oconfig_value (jvm_env, ci->values[i]); + if (o_value == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: " + "Creating an OConfigValue object failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem); + return (NULL); + } + + (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addvalue, o_value); + (*jvm_env)->DeleteLocalRef (jvm_env, o_value); + } /* }}} for (i = 0; i < ci->values_num; i++) */ + + /* Call OConfigItem.addChild for each child */ + for (i = 0; i < ci->children_num; i++) /* {{{ */ + { + jobject o_child; + + o_child = ctoj_oconfig_item (jvm_env, ci->children + i); + if (o_child == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: " + "Creating an OConfigItem object failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem); + return (NULL); + } + + (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addchild, o_child); + (*jvm_env)->DeleteLocalRef (jvm_env, o_child); + } /* }}} for (i = 0; i < ci->children_num; i++) */ + + return (o_ocitem); +} /* }}} jobject ctoj_oconfig_item */ + +/* Convert a data_set_t to a org/collectd/api/DataSet */ +static jobject ctoj_data_set (JNIEnv *jvm_env, const data_set_t *ds) /* {{{ */ +{ + jclass c_dataset; + jmethodID m_constructor; + jmethodID m_add; + jobject o_type; + jobject o_dataset; + int i; + + /* Look up the org/collectd/api/DataSet class */ + c_dataset = (*jvm_env)->FindClass (jvm_env, "org/collectd/api/DataSet"); + if (c_dataset == NULL) + { + ERROR ("java plugin: ctoj_data_set: Looking up the " + "org/collectd/api/DataSet class failed."); + return (NULL); + } + + /* Search for the `DataSet (String type)' constructor. */ + m_constructor = (*jvm_env)->GetMethodID (jvm_env, + c_dataset, "", "(Ljava.lang.String;)V"); + if (m_constructor == NULL) + { + ERROR ("java plugin: ctoj_data_set: Looking up the " + "`DataSet (String)' constructor failed."); + return (NULL); + } + + /* Search for the `void addDataSource (DataSource)' method. */ + m_add = (*jvm_env)->GetMethodID (jvm_env, + c_dataset, "addDataSource", "(Lorg/collectd/api/DataSource;)V"); + if (m_add == NULL) + { + ERROR ("java plugin: ctoj_data_set: Looking up the " + "`addDataSource (DataSource)' method failed."); + return (NULL); + } + + o_type = (*jvm_env)->NewStringUTF (jvm_env, ds->type); + if (o_type == NULL) + { + ERROR ("java plugin: ctoj_data_set: Creating a String object failed."); + return (NULL); + } + + o_dataset = (*jvm_env)->NewObject (jvm_env, + c_dataset, m_constructor, o_type); + if (o_dataset == NULL) + { + ERROR ("java plugin: ctoj_data_set: Creating a DataSet object failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_type); + return (NULL); + } + + /* Decrease reference counter on the java.lang.String object. */ + (*jvm_env)->DeleteLocalRef (jvm_env, o_type); + + for (i = 0; i < ds->ds_num; i++) + { + jobject o_datasource; + + o_datasource = ctoj_data_source (jvm_env, ds->ds + i); + if (o_datasource == NULL) + { + ERROR ("java plugin: ctoj_data_set: ctoj_data_source (%s.%s) failed", + ds->type, ds->ds[i].name); + (*jvm_env)->DeleteLocalRef (jvm_env, o_dataset); + return (NULL); + } + + (*jvm_env)->CallVoidMethod (jvm_env, o_dataset, m_add, o_datasource); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource); + } /* for (i = 0; i < ds->ds_num; i++) */ + + return (o_dataset); +} /* }}} jobject ctoj_data_set */ + +static int ctoj_value_list_add_value (JNIEnv *jvm_env, /* {{{ */ + value_t value, int ds_type, + jclass class_ptr, jobject object_ptr) +{ + jmethodID m_addvalue; + jobject o_number; + + m_addvalue = (*jvm_env)->GetMethodID (jvm_env, class_ptr, + "addValue", "(Ljava/lang/Number;)V"); + if (m_addvalue == NULL) + { + ERROR ("java plugin: ctoj_value_list_add_value: " + "Cannot find method `void addValue (Number)'."); + return (-1); + } + + o_number = ctoj_value_to_number (jvm_env, value, ds_type); + if (o_number == NULL) + { + ERROR ("java plugin: ctoj_value_list_add_value: " + "ctoj_value_to_number failed."); + return (-1); + } + + (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_addvalue, o_number); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_number); + + return (0); +} /* }}} int ctoj_value_list_add_value */ + +static int ctoj_value_list_add_data_set (JNIEnv *jvm_env, /* {{{ */ + jclass c_valuelist, jobject o_valuelist, const data_set_t *ds) +{ + jmethodID m_setdataset; + jobject o_dataset; + + /* Look for the `void setDataSource (List ds)' method. */ + m_setdataset = (*jvm_env)->GetMethodID (jvm_env, c_valuelist, + "setDataSet", "(Lorg/collectd/api/DataSet;)V"); + if (m_setdataset == NULL) + { + ERROR ("java plugin: ctoj_value_list_add_data_set: " + "Cannot find the `void setDataSet (DataSet)' method."); + return (-1); + } + + /* Create a DataSet object. */ + o_dataset = ctoj_data_set (jvm_env, ds); + if (o_dataset == NULL) + { + ERROR ("java plugin: ctoj_value_list_add_data_set: " + "ctoj_data_set (%s) failed.", ds->type); + return (-1); + } + + /* Actually call the method. */ + (*jvm_env)->CallVoidMethod (jvm_env, + o_valuelist, m_setdataset, o_dataset); + + /* Decrease reference counter on the List object. */ + (*jvm_env)->DeleteLocalRef (jvm_env, o_dataset); + + return (0); +} /* }}} int ctoj_value_list_add_data_set */ + +/* Convert a value_list_t (and data_set_t) to a org/collectd/api/ValueList */ +static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */ + const data_set_t *ds, const value_list_t *vl) +{ + jclass c_valuelist; + jmethodID m_valuelist_constructor; + jobject o_valuelist; + int status; + int i; + + /* First, create a new ValueList instance.. + * Look up the class.. */ + c_valuelist = (*jvm_env)->FindClass (jvm_env, + "org/collectd/api/ValueList"); + if (c_valuelist == NULL) + { + ERROR ("java plugin: ctoj_value_list: " + "FindClass (org/collectd/api/ValueList) failed."); + return (NULL); + } + + /* Lookup the `ValueList ()' constructor. */ + m_valuelist_constructor = (*jvm_env)->GetMethodID (jvm_env, c_valuelist, + "", "()V"); + if (m_valuelist_constructor == NULL) + { + ERROR ("java plugin: ctoj_value_list: Cannot find the " + "`ValueList ()' constructor."); + return (NULL); + } + + /* Create a new instance. */ + o_valuelist = (*jvm_env)->NewObject (jvm_env, c_valuelist, + m_valuelist_constructor); + if (o_valuelist == NULL) + { + ERROR ("java plugin: ctoj_value_list: Creating a new ValueList instance " + "failed."); + return (NULL); + } + + status = ctoj_value_list_add_data_set (jvm_env, + c_valuelist, o_valuelist, ds); + if (status != 0) + { + ERROR ("java plugin: ctoj_value_list: " + "ctoj_value_list_add_data_set failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist); + return (NULL); + } + + /* Set the strings.. */ +#define SET_STRING(str,method_name) do { \ + status = ctoj_string (jvm_env, str, \ + c_valuelist, o_valuelist, method_name); \ + if (status != 0) { \ + ERROR ("java plugin: ctoj_value_list: ctoj_string (%s) failed.", \ + method_name); \ + (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist); \ + return (NULL); \ + } } while (0) + + SET_STRING (vl->host, "setHost"); + SET_STRING (vl->plugin, "setPlugin"); + SET_STRING (vl->plugin_instance, "setPluginInstance"); + SET_STRING (vl->type, "setType"); + SET_STRING (vl->type_instance, "setTypeInstance"); + +#undef SET_STRING + + /* Set the `time' member. Java stores time in milliseconds. */ + status = ctoj_long (jvm_env, ((jlong) vl->time) * ((jlong) 1000), + c_valuelist, o_valuelist, "setTime"); + if (status != 0) + { + ERROR ("java plugin: ctoj_value_list: ctoj_long (setTime) failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist); + return (NULL); + } + + /* Set the `interval' member.. */ + status = ctoj_long (jvm_env, (jlong) vl->interval, + c_valuelist, o_valuelist, "setInterval"); + if (status != 0) + { + ERROR ("java plugin: ctoj_value_list: ctoj_long (setInterval) failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist); + return (NULL); + } + + for (i = 0; i < vl->values_len; i++) + { + status = ctoj_value_list_add_value (jvm_env, vl->values[i], ds->ds[i].type, + c_valuelist, o_valuelist); + if (status != 0) + { + ERROR ("java plugin: ctoj_value_list: " + "ctoj_value_list_add_value failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist); + return (NULL); + } + } + + return (o_valuelist); +} /* }}} jobject ctoj_value_list */ + +/* Convert a notification_t to a org/collectd/api/Notification */ +static jobject ctoj_notification (JNIEnv *jvm_env, /* {{{ */ + const notification_t *n) +{ + jclass c_notification; + jmethodID m_constructor; + jobject o_notification; + int status; + + /* First, create a new Notification instance.. + * Look up the class.. */ + c_notification = (*jvm_env)->FindClass (jvm_env, + "org/collectd/api/Notification"); + if (c_notification == NULL) + { + ERROR ("java plugin: ctoj_notification: " + "FindClass (org/collectd/api/Notification) failed."); + return (NULL); + } + + /* Lookup the `Notification ()' constructor. */ + m_constructor = (*jvm_env)->GetMethodID (jvm_env, c_notification, + "", "()V"); + if (m_constructor == NULL) + { + ERROR ("java plugin: ctoj_notification: Cannot find the " + "`Notification ()' constructor."); + return (NULL); + } + + /* Create a new instance. */ + o_notification = (*jvm_env)->NewObject (jvm_env, c_notification, + m_constructor); + if (o_notification == NULL) + { + ERROR ("java plugin: ctoj_notification: Creating a new Notification " + "instance failed."); + return (NULL); + } + + /* Set the strings.. */ +#define SET_STRING(str,method_name) do { \ + status = ctoj_string (jvm_env, str, \ + c_notification, o_notification, method_name); \ + if (status != 0) { \ + ERROR ("java plugin: ctoj_notification: ctoj_string (%s) failed.", \ + method_name); \ + (*jvm_env)->DeleteLocalRef (jvm_env, o_notification); \ + return (NULL); \ + } } while (0) + + SET_STRING (n->host, "setHost"); + SET_STRING (n->plugin, "setPlugin"); + SET_STRING (n->plugin_instance, "setPluginInstance"); + SET_STRING (n->type, "setType"); + SET_STRING (n->type_instance, "setTypeInstance"); + SET_STRING (n->message, "setMessage"); + +#undef SET_STRING + + /* Set the `time' member. Java stores time in milliseconds. */ + status = ctoj_long (jvm_env, ((jlong) n->time) * ((jlong) 1000), + c_notification, o_notification, "setTime"); + if (status != 0) + { + ERROR ("java plugin: ctoj_notification: ctoj_long (setTime) failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_notification); + return (NULL); + } + + /* Set the `interval' member.. */ + status = ctoj_int (jvm_env, (jint) n->severity, + c_notification, o_notification, "setSeverity"); + if (status != 0) + { + ERROR ("java plugin: ctoj_notification: ctoj_int (setSeverity) failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_notification); + return (NULL); + } + + return (o_notification); +} /* }}} jobject ctoj_notification */ + +/* + * Java to C conversion functions + */ +/* Call a `String ()' method. */ +static int jtoc_string (JNIEnv *jvm_env, /* {{{ */ + char *buffer, size_t buffer_size, int empty_okay, + jclass class_ptr, jobject object_ptr, const char *method_name) +{ + jmethodID method_id; + jobject string_obj; + const char *c_str; + + method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr, + method_name, "()Ljava/lang/String;"); + if (method_id == NULL) + { + ERROR ("java plugin: jtoc_string: Cannot find method `String %s ()'.", + method_name); + return (-1); + } + + string_obj = (*jvm_env)->CallObjectMethod (jvm_env, object_ptr, method_id); + if ((string_obj == NULL) && (empty_okay == 0)) + { + ERROR ("java plugin: jtoc_string: CallObjectMethod (%s) failed.", + method_name); + return (-1); + } + else if ((string_obj == NULL) && (empty_okay != 0)) + { + memset (buffer, 0, buffer_size); + return (0); + } + + c_str = (*jvm_env)->GetStringUTFChars (jvm_env, string_obj, 0); + if (c_str == NULL) + { + ERROR ("java plugin: jtoc_string: GetStringUTFChars failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, string_obj); + return (-1); + } + + sstrncpy (buffer, c_str, buffer_size); + + (*jvm_env)->ReleaseStringUTFChars (jvm_env, string_obj, c_str); + (*jvm_env)->DeleteLocalRef (jvm_env, string_obj); + + return (0); +} /* }}} int jtoc_string */ + +/* Call an `int ()' method. */ +static int jtoc_int (JNIEnv *jvm_env, /* {{{ */ + jint *ret_value, + jclass class_ptr, jobject object_ptr, const char *method_name) +{ + jmethodID method_id; + + method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr, + method_name, "()I"); + if (method_id == NULL) + { + ERROR ("java plugin: jtoc_int: Cannot find method `int %s ()'.", + method_name); + return (-1); + } + + *ret_value = (*jvm_env)->CallIntMethod (jvm_env, object_ptr, method_id); + + return (0); +} /* }}} int jtoc_int */ + +/* Call a `long ()' method. */ +static int jtoc_long (JNIEnv *jvm_env, /* {{{ */ + jlong *ret_value, + jclass class_ptr, jobject object_ptr, const char *method_name) +{ + jmethodID method_id; + + method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr, + method_name, "()J"); + if (method_id == NULL) + { + ERROR ("java plugin: jtoc_long: Cannot find method `long %s ()'.", + method_name); + return (-1); + } + + *ret_value = (*jvm_env)->CallLongMethod (jvm_env, object_ptr, method_id); + + return (0); +} /* }}} int jtoc_long */ + +/* Call a `double ()' method. */ +static int jtoc_double (JNIEnv *jvm_env, /* {{{ */ + jdouble *ret_value, + jclass class_ptr, jobject object_ptr, const char *method_name) +{ + jmethodID method_id; + + method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr, + method_name, "()D"); + if (method_id == NULL) + { + ERROR ("java plugin: jtoc_double: Cannot find method `double %s ()'.", + method_name); + return (-1); + } + + *ret_value = (*jvm_env)->CallDoubleMethod (jvm_env, object_ptr, method_id); + + return (0); +} /* }}} int jtoc_double */ + +static int jtoc_value (JNIEnv *jvm_env, /* {{{ */ + value_t *ret_value, int ds_type, jobject object_ptr) +{ + jclass class_ptr; + int status; + + class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr); + + if (ds_type == DS_TYPE_COUNTER) + { + jlong tmp_long; + + status = jtoc_long (jvm_env, &tmp_long, + class_ptr, object_ptr, "longValue"); + if (status != 0) + { + ERROR ("java plugin: jtoc_value: " + "jtoc_long failed."); + return (-1); + } + (*ret_value).counter = (counter_t) tmp_long; + } + else + { + jdouble tmp_double; + + status = jtoc_double (jvm_env, &tmp_double, + class_ptr, object_ptr, "doubleValue"); + if (status != 0) + { + ERROR ("java plugin: jtoc_value: " + "jtoc_double failed."); + return (-1); + } + (*ret_value).gauge = (gauge_t) tmp_double; + } + + return (0); +} /* }}} int jtoc_value */ + +/* Read a List, convert it to `value_t' and add it to the given + * `value_list_t'. */ +static int jtoc_values_array (JNIEnv *jvm_env, /* {{{ */ + const data_set_t *ds, value_list_t *vl, + jclass class_ptr, jobject object_ptr) +{ + jmethodID m_getvalues; + jmethodID m_toarray; + jobject o_list; + jobjectArray o_number_array; + + value_t *values; + int values_num; + int i; + + values_num = ds->ds_num; + + values = NULL; + o_number_array = NULL; + o_list = NULL; + +#define BAIL_OUT(status) \ + free (values); \ + if (o_number_array != NULL) \ + (*jvm_env)->DeleteLocalRef (jvm_env, o_number_array); \ + if (o_list != NULL) \ + (*jvm_env)->DeleteLocalRef (jvm_env, o_list); \ + return (status); + + /* Call: List ValueList.getValues () */ + m_getvalues = (*jvm_env)->GetMethodID (jvm_env, class_ptr, + "getValues", "()Ljava/util/List;"); + if (m_getvalues == NULL) + { + ERROR ("java plugin: jtoc_values_array: " + "Cannot find method `List getValues ()'."); + BAIL_OUT (-1); + } + + o_list = (*jvm_env)->CallObjectMethod (jvm_env, object_ptr, m_getvalues); + if (o_list == NULL) + { + ERROR ("java plugin: jtoc_values_array: " + "CallObjectMethod (getValues) failed."); + BAIL_OUT (-1); + } + + /* Call: Number[] List.toArray () */ + m_toarray = (*jvm_env)->GetMethodID (jvm_env, + (*jvm_env)->GetObjectClass (jvm_env, o_list), + "toArray", "()[Ljava/lang/Object;"); + if (m_toarray == NULL) + { + ERROR ("java plugin: jtoc_values_array: " + "Cannot find method `Object[] toArray ()'."); + BAIL_OUT (-1); + } + + o_number_array = (*jvm_env)->CallObjectMethod (jvm_env, o_list, m_toarray); + if (o_number_array == NULL) + { + ERROR ("java plugin: jtoc_values_array: " + "CallObjectMethod (toArray) failed."); + BAIL_OUT (-1); + } + + values = (value_t *) calloc (values_num, sizeof (value_t)); + if (values == NULL) + { + ERROR ("java plugin: jtoc_values_array: calloc failed."); + BAIL_OUT (-1); + } + + for (i = 0; i < values_num; i++) + { + jobject o_number; + int status; + + o_number = (*jvm_env)->GetObjectArrayElement (jvm_env, + o_number_array, (jsize) i); + if (o_number == NULL) + { + ERROR ("java plugin: jtoc_values_array: " + "GetObjectArrayElement (%i) failed.", i); + BAIL_OUT (-1); + } + + status = jtoc_value (jvm_env, values + i, ds->ds[i].type, o_number); + if (status != 0) + { + ERROR ("java plugin: jtoc_values_array: " + "jtoc_value (%i) failed.", i); + BAIL_OUT (-1); + } + } /* for (i = 0; i < values_num; i++) */ + + vl->values = values; + vl->values_len = values_num; + +#undef BAIL_OUT + (*jvm_env)->DeleteLocalRef (jvm_env, o_number_array); + (*jvm_env)->DeleteLocalRef (jvm_env, o_list); + return (0); +} /* }}} int jtoc_values_array */ + +/* Convert a org/collectd/api/ValueList to a value_list_t. */ +static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */ + jobject object_ptr) +{ + jclass class_ptr; + int status; + jlong tmp_long; + const data_set_t *ds; + + class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr); + if (class_ptr == NULL) + { + ERROR ("java plugin: jtoc_value_list: GetObjectClass failed."); + return (-1); + } + + /* eo == empty okay */ +#define SET_STRING(buffer,method, eo) do { \ + status = jtoc_string (jvm_env, buffer, sizeof (buffer), eo, \ + class_ptr, object_ptr, method); \ + if (status != 0) { \ + ERROR ("java plugin: jtoc_value_list: jtoc_string (%s) failed.", \ + method); \ + return (-1); \ + } } while (0) + + SET_STRING(vl->type, "getType", /* empty = */ 0); + + ds = plugin_get_ds (vl->type); + if (ds == NULL) + { + ERROR ("java plugin: jtoc_value_list: Data-set `%s' is not defined. " + "Please consult the types.db(5) manpage for mor information.", + vl->type); + return (-1); + } + + SET_STRING(vl->host, "getHost", /* empty = */ 0); + SET_STRING(vl->plugin, "getPlugin", /* empty = */ 0); + SET_STRING(vl->plugin_instance, "getPluginInstance", /* empty = */ 1); + SET_STRING(vl->type_instance, "getTypeInstance", /* empty = */ 1); + +#undef SET_STRING + + status = jtoc_long (jvm_env, &tmp_long, class_ptr, object_ptr, "getTime"); + if (status != 0) + { + ERROR ("java plugin: jtoc_value_list: jtoc_long (getTime) failed."); + return (-1); + } + /* Java measures time in milliseconds. */ + vl->time = (time_t) (tmp_long / ((jlong) 1000)); + + status = jtoc_long (jvm_env, &tmp_long, + class_ptr, object_ptr, "getInterval"); + if (status != 0) + { + ERROR ("java plugin: jtoc_value_list: jtoc_long (getInterval) failed."); + return (-1); + } + vl->interval = (int) tmp_long; + + status = jtoc_values_array (jvm_env, ds, vl, class_ptr, object_ptr); + if (status != 0) + { + ERROR ("java plugin: jtoc_value_list: jtoc_values_array failed."); + return (-1); + } + + return (0); +} /* }}} int jtoc_value_list */ + +/* Convert a org/collectd/api/Notification to a notification_t. */ +static int jtoc_notification (JNIEnv *jvm_env, notification_t *n, /* {{{ */ + jobject object_ptr) +{ + jclass class_ptr; + int status; + jlong tmp_long; + jint tmp_int; + + class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr); + if (class_ptr == NULL) + { + ERROR ("java plugin: jtoc_notification: GetObjectClass failed."); + return (-1); + } + + /* eo == empty okay */ +#define SET_STRING(buffer,method, eo) do { \ + status = jtoc_string (jvm_env, buffer, sizeof (buffer), eo, \ + class_ptr, object_ptr, method); \ + if (status != 0) { \ + ERROR ("java plugin: jtoc_notification: jtoc_string (%s) failed.", \ + method); \ + return (-1); \ + } } while (0) + + SET_STRING (n->host, "getHost", /* empty = */ 1); + SET_STRING (n->plugin, "getPlugin", /* empty = */ 1); + SET_STRING (n->plugin_instance, "getPluginInstance", /* empty = */ 1); + SET_STRING (n->type, "getType", /* empty = */ 1); + SET_STRING (n->type_instance, "getTypeInstance", /* empty = */ 1); + SET_STRING (n->message, "getMessage", /* empty = */ 0); + +#undef SET_STRING + + status = jtoc_long (jvm_env, &tmp_long, class_ptr, object_ptr, "getTime"); + if (status != 0) + { + ERROR ("java plugin: jtoc_notification: jtoc_long (getTime) failed."); + return (-1); + } + /* Java measures time in milliseconds. */ + n->time = (time_t) (tmp_long / ((jlong) 1000)); + + status = jtoc_int (jvm_env, &tmp_int, + class_ptr, object_ptr, "getSeverity"); + if (status != 0) + { + ERROR ("java plugin: jtoc_notification: jtoc_int (getSeverity) failed."); + return (-1); + } + n->severity = (int) tmp_int; + + return (0); +} /* }}} int jtoc_notification */ +/* + * Functions accessible from Java + */ +static jint JNICALL cjni_api_dispatch_values (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject java_vl) +{ + value_list_t vl = VALUE_LIST_INIT; + int status; + + DEBUG ("cjni_api_dispatch_values: java_vl = %p;", (void *) java_vl); + + status = jtoc_value_list (jvm_env, &vl, java_vl); + if (status != 0) + { + ERROR ("java plugin: cjni_api_dispatch_values: jtoc_value_list failed."); + return (-1); + } + + status = plugin_dispatch_values (&vl); + + sfree (vl.values); + + return (status); +} /* }}} jint cjni_api_dispatch_values */ + +static jint JNICALL cjni_api_dispatch_notification (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_notification) +{ + notification_t n; + int status; + + memset (&n, 0, sizeof (n)); + n.meta = NULL; + + status = jtoc_notification (jvm_env, &n, o_notification); + if (status != 0) + { + ERROR ("java plugin: cjni_api_dispatch_notification: jtoc_notification failed."); + return (-1); + } + + status = plugin_dispatch_notification (&n); + + return (status); +} /* }}} jint cjni_api_dispatch_notification */ + +static jobject JNICALL cjni_api_get_ds (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_string_type) +{ + const char *ds_name; + const data_set_t *ds; + jobject o_dataset; + + ds_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_string_type, 0); + if (ds_name == NULL) + { + ERROR ("java plugin: cjni_api_get_ds: GetStringUTFChars failed."); + return (NULL); + } + + ds = plugin_get_ds (ds_name); + DEBUG ("java plugin: cjni_api_get_ds: " + "plugin_get_ds (%s) = %p;", ds_name, (void *) ds); + + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_string_type, ds_name); + + if (ds == NULL) + return (NULL); + + o_dataset = ctoj_data_set (jvm_env, ds); + return (o_dataset); +} /* }}} jint cjni_api_get_ds */ + +static jint JNICALL cjni_api_register_config (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_config) +{ + return (cjni_callback_register (jvm_env, o_name, o_config, CB_TYPE_CONFIG)); +} /* }}} jint cjni_api_register_config */ + +static jint JNICALL cjni_api_register_init (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_config) +{ + return (cjni_callback_register (jvm_env, o_name, o_config, CB_TYPE_INIT)); +} /* }}} jint cjni_api_register_init */ + +static jint JNICALL cjni_api_register_read (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_read) +{ + user_data_t ud; + cjni_callback_info_t *cbi; + + cbi = cjni_callback_info_create (jvm_env, o_name, o_read, CB_TYPE_READ); + if (cbi == NULL) + return (-1); + + DEBUG ("java plugin: Registering new read callback: %s", cbi->name); + + memset (&ud, 0, sizeof (ud)); + ud.data = (void *) cbi; + ud.free_func = cjni_callback_info_destroy; + + plugin_register_complex_read (cbi->name, cjni_read, + /* interval = */ NULL, &ud); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_read); + + return (0); +} /* }}} jint cjni_api_register_read */ + +static jint JNICALL cjni_api_register_write (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_write) +{ + user_data_t ud; + cjni_callback_info_t *cbi; + + cbi = cjni_callback_info_create (jvm_env, o_name, o_write, CB_TYPE_WRITE); + if (cbi == NULL) + return (-1); + + DEBUG ("java plugin: Registering new write callback: %s", cbi->name); + + memset (&ud, 0, sizeof (ud)); + ud.data = (void *) cbi; + ud.free_func = cjni_callback_info_destroy; + + plugin_register_write (cbi->name, cjni_write, &ud); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_write); + + return (0); +} /* }}} jint cjni_api_register_write */ + +static jint JNICALL cjni_api_register_flush (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_flush) +{ + user_data_t ud; + cjni_callback_info_t *cbi; + + cbi = cjni_callback_info_create (jvm_env, o_name, o_flush, CB_TYPE_FLUSH); + if (cbi == NULL) + return (-1); + + DEBUG ("java plugin: Registering new flush callback: %s", cbi->name); + + memset (&ud, 0, sizeof (ud)); + ud.data = (void *) cbi; + ud.free_func = cjni_callback_info_destroy; + + plugin_register_flush (cbi->name, cjni_flush, &ud); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_flush); + + return (0); +} /* }}} jint cjni_api_register_flush */ + +static jint JNICALL cjni_api_register_shutdown (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_shutdown) +{ + return (cjni_callback_register (jvm_env, o_name, o_shutdown, + CB_TYPE_SHUTDOWN)); +} /* }}} jint cjni_api_register_shutdown */ + +static jint JNICALL cjni_api_register_log (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_log) +{ + user_data_t ud; + cjni_callback_info_t *cbi; + + cbi = cjni_callback_info_create (jvm_env, o_name, o_log, CB_TYPE_LOG); + if (cbi == NULL) + return (-1); + + DEBUG ("java plugin: Registering new log callback: %s", cbi->name); + + memset (&ud, 0, sizeof (ud)); + ud.data = (void *) cbi; + ud.free_func = cjni_callback_info_destroy; + + plugin_register_log (cbi->name, cjni_log, &ud); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_log); + + return (0); +} /* }}} jint cjni_api_register_log */ + +static jint JNICALL cjni_api_register_notification (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_notification) +{ + user_data_t ud; + cjni_callback_info_t *cbi; + + cbi = cjni_callback_info_create (jvm_env, o_name, o_notification, + CB_TYPE_NOTIFICATION); + if (cbi == NULL) + return (-1); + + DEBUG ("java plugin: Registering new notification callback: %s", cbi->name); + + memset (&ud, 0, sizeof (ud)); + ud.data = (void *) cbi; + ud.free_func = cjni_callback_info_destroy; + + plugin_register_notification (cbi->name, cjni_notification, &ud); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_notification); + + return (0); +} /* }}} jint cjni_api_register_notification */ + +static jint JNICALL cjni_api_register_match_target (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_match, int type) +{ + int status; + const char *c_name; + + c_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_name, 0); + if (c_name == NULL) + { + ERROR ("java plugin: cjni_api_register_match_target: " + "GetStringUTFChars failed."); + return (-1); + } + + status = cjni_callback_register (jvm_env, o_name, o_match, type); + if (status != 0) + { + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name); + return (-1); + } + + if (type == CB_TYPE_MATCH) + { + match_proc_t m_proc; + + memset (&m_proc, 0, sizeof (m_proc)); + m_proc.create = cjni_match_target_create; + m_proc.destroy = cjni_match_target_destroy; + m_proc.match = (void *) cjni_match_target_invoke; + + status = fc_register_match (c_name, m_proc); + } + else if (type == CB_TYPE_TARGET) + { + target_proc_t t_proc; + + memset (&t_proc, 0, sizeof (t_proc)); + t_proc.create = cjni_match_target_create; + t_proc.destroy = cjni_match_target_destroy; + t_proc.invoke = cjni_match_target_invoke; + + status = fc_register_target (c_name, t_proc); + } + else + { + ERROR ("java plugin: cjni_api_register_match_target: " + "Don't know whether to create a match or a target."); + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name); + return (-1); + } + + if (status != 0) + { + ERROR ("java plugin: cjni_api_register_match_target: " + "%s failed.", + (type == CB_TYPE_MATCH) ? "fc_register_match" : "fc_register_target"); + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name); + return (-1); + } + + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name); + + return (0); +} /* }}} jint cjni_api_register_match_target */ + +static jint JNICALL cjni_api_register_match (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_match) +{ + return (cjni_api_register_match_target (jvm_env, this, o_name, o_match, + CB_TYPE_MATCH)); +} /* }}} jint cjni_api_register_match */ + +static jint JNICALL cjni_api_register_target (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_target) +{ + return (cjni_api_register_match_target (jvm_env, this, o_name, o_target, + CB_TYPE_TARGET)); +} /* }}} jint cjni_api_register_target */ + +static void JNICALL cjni_api_log (JNIEnv *jvm_env, /* {{{ */ + jobject this, jint severity, jobject o_message) +{ + const char *c_str; + + c_str = (*jvm_env)->GetStringUTFChars (jvm_env, o_message, 0); + if (c_str == NULL) + { + ERROR ("java plugin: cjni_api_log: GetStringUTFChars failed."); + return; + } + + if (severity < LOG_ERR) + severity = LOG_ERR; + if (severity > LOG_DEBUG) + severity = LOG_DEBUG; + + plugin_log (severity, "%s", c_str); + + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_message, c_str); +} /* }}} void cjni_api_log */ + +/* List of ``native'' functions, i. e. C-functions that can be called from + * Java. */ +static JNINativeMethod jni_api_functions[] = /* {{{ */ +{ + { "dispatchValues", + "(Lorg/collectd/api/ValueList;)I", + cjni_api_dispatch_values }, + + { "dispatchNotification", + "(Lorg/collectd/api/Notification;)I", + cjni_api_dispatch_notification }, + + { "getDS", + "(Ljava/lang/String;)Lorg/collectd/api/DataSet;", + cjni_api_get_ds }, + + { "registerConfig", + "(Ljava/lang/String;Lorg/collectd/api/CollectdConfigInterface;)I", + cjni_api_register_config }, + + { "registerInit", + "(Ljava/lang/String;Lorg/collectd/api/CollectdInitInterface;)I", + cjni_api_register_init }, + + { "registerRead", + "(Ljava/lang/String;Lorg/collectd/api/CollectdReadInterface;)I", + cjni_api_register_read }, + + { "registerWrite", + "(Ljava/lang/String;Lorg/collectd/api/CollectdWriteInterface;)I", + cjni_api_register_write }, + + { "registerFlush", + "(Ljava/lang/String;Lorg/collectd/api/CollectdFlushInterface;)I", + cjni_api_register_flush }, + + { "registerShutdown", + "(Ljava/lang/String;Lorg/collectd/api/CollectdShutdownInterface;)I", + cjni_api_register_shutdown }, + + { "registerLog", + "(Ljava/lang/String;Lorg/collectd/api/CollectdLogInterface;)I", + cjni_api_register_log }, + + { "registerNotification", + "(Ljava/lang/String;Lorg/collectd/api/CollectdNotificationInterface;)I", + cjni_api_register_notification }, + + { "registerMatch", + "(Ljava/lang/String;Lorg/collectd/api/CollectdMatchFactoryInterface;)I", + cjni_api_register_match }, + + { "registerTarget", + "(Ljava/lang/String;Lorg/collectd/api/CollectdTargetFactoryInterface;)I", + cjni_api_register_target }, + + { "log", + "(ILjava/lang/String;)V", + cjni_api_log }, +}; +static size_t jni_api_functions_num = sizeof (jni_api_functions) + / sizeof (jni_api_functions[0]); +/* }}} */ + +/* + * Functions + */ +/* Allocate a `cjni_callback_info_t' given the type and objects necessary for + * all registration functions. */ +static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, /* {{{ */ + jobject o_name, jobject o_callback, int type) +{ + const char *c_name; + cjni_callback_info_t *cbi; + const char *method_name; + const char *method_signature; + + switch (type) + { + case CB_TYPE_CONFIG: + method_name = "config"; + method_signature = "(Lorg/collectd/api/OConfigItem;)I"; + break; + + case CB_TYPE_INIT: + method_name = "init"; + method_signature = "()I"; + break; + + case CB_TYPE_READ: + method_name = "read"; + method_signature = "()I"; + break; + + case CB_TYPE_WRITE: + method_name = "write"; + method_signature = "(Lorg/collectd/api/ValueList;)I"; + break; + + case CB_TYPE_FLUSH: + method_name = "flush"; + method_signature = "(ILjava/lang/String;)I"; + break; + + case CB_TYPE_SHUTDOWN: + method_name = "shutdown"; + method_signature = "()I"; + break; + + case CB_TYPE_LOG: + method_name = "log"; + method_signature = "(ILjava/lang/String;)V"; + break; + + case CB_TYPE_NOTIFICATION: + method_name = "notification"; + method_signature = "(Lorg/collectd/api/Notification;)I"; + break; + + case CB_TYPE_MATCH: + method_name = "createMatch"; + method_signature = "(Lorg/collectd/api/OConfigItem;)" + "Lorg/collectd/api/CollectdMatchInterface;"; + break; + + case CB_TYPE_TARGET: + method_name = "createTarget"; + method_signature = "(Lorg/collectd/api/OConfigItem;)" + "Lorg/collectd/api/CollectdTargetInterface;"; + break; + + default: + ERROR ("java plugin: cjni_callback_info_create: Unknown type: %#x", + type); + return (NULL); + } + + c_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_name, 0); + if (c_name == NULL) + { + ERROR ("java plugin: cjni_callback_info_create: " + "GetStringUTFChars failed."); + return (NULL); + } + + cbi = (cjni_callback_info_t *) malloc (sizeof (*cbi)); + if (cbi == NULL) + { + ERROR ("java plugin: cjni_callback_info_create: malloc failed."); + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name); + return (NULL); + } + memset (cbi, 0, sizeof (*cbi)); + cbi->type = type; + + cbi->name = strdup (c_name); + if (cbi->name == NULL) + { + pthread_mutex_unlock (&java_callbacks_lock); + ERROR ("java plugin: cjni_callback_info_create: strdup failed."); + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name); + return (NULL); + } + + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name); + + cbi->object = (*jvm_env)->NewGlobalRef (jvm_env, o_callback); + if (cbi->object == NULL) + { + ERROR ("java plugin: cjni_callback_info_create: NewGlobalRef failed."); + free (cbi); + return (NULL); + } + + cbi->class = (*jvm_env)->GetObjectClass (jvm_env, cbi->object); + if (cbi->class == NULL) + { + ERROR ("java plugin: cjni_callback_info_create: GetObjectClass failed."); + free (cbi); + return (NULL); + } + + cbi->method = (*jvm_env)->GetMethodID (jvm_env, cbi->class, + method_name, method_signature); + if (cbi->method == NULL) + { + ERROR ("java plugin: cjni_callback_info_create: " + "Cannot find the `%s' method with signature `%s'.", + method_name, method_signature); + free (cbi); + return (NULL); + } + + return (cbi); +} /* }}} cjni_callback_info_t cjni_callback_info_create */ + +/* Allocate a `cjni_callback_info_t' via `cjni_callback_info_create' and add it + * to the global `java_callbacks' variable. This is used for `config', `init', + * and `shutdown' callbacks. */ +static int cjni_callback_register (JNIEnv *jvm_env, /* {{{ */ + jobject o_name, jobject o_callback, int type) +{ + cjni_callback_info_t *cbi; + cjni_callback_info_t *tmp; +#if COLLECT_DEBUG + const char *type_str; +#endif + + cbi = cjni_callback_info_create (jvm_env, o_name, o_callback, type); + if (cbi == NULL) + return (-1); + +#if COLLECT_DEBUG + switch (type) + { + case CB_TYPE_CONFIG: + type_str = "config"; + break; + + case CB_TYPE_INIT: + type_str = "init"; + break; + + case CB_TYPE_SHUTDOWN: + type_str = "shutdown"; + break; + + case CB_TYPE_MATCH: + type_str = "match"; + break; + + case CB_TYPE_TARGET: + type_str = "target"; + break; + + default: + type_str = ""; + } + DEBUG ("java plugin: Registering new %s callback: %s", + type_str, cbi->name); +#endif + + pthread_mutex_lock (&java_callbacks_lock); + + tmp = (cjni_callback_info_t *) realloc (java_callbacks, + (java_callbacks_num + 1) * sizeof (*java_callbacks)); + if (tmp == NULL) + { + pthread_mutex_unlock (&java_callbacks_lock); + ERROR ("java plugin: cjni_callback_register: realloc failed."); + + (*jvm_env)->DeleteGlobalRef (jvm_env, cbi->object); + free (cbi); + + return (-1); + } + java_callbacks = tmp; + java_callbacks[java_callbacks_num] = *cbi; + java_callbacks_num++; + + pthread_mutex_unlock (&java_callbacks_lock); + + free (cbi); + return (0); +} /* }}} int cjni_callback_register */ + +/* Callback for `pthread_key_create'. It frees the data contained in + * `jvm_env_key' and prints a warning if the reference counter is not zero. */ +static void cjni_jvm_env_destroy (void *args) /* {{{ */ +{ + cjni_jvm_env_t *cjni_env; + + if (args == NULL) + return; + + cjni_env = (cjni_jvm_env_t *) args; + + if (cjni_env->reference_counter > 0) + { + ERROR ("java plugin: cjni_jvm_env_destroy: " + "cjni_env->reference_counter = %i;", cjni_env->reference_counter); + } + + if (cjni_env->jvm_env != NULL) + { + ERROR ("java plugin: cjni_jvm_env_destroy: cjni_env->jvm_env = %p;", + (void *) cjni_env->jvm_env); + } + + /* The pointer is allocated in `cjni_thread_attach' */ + free (cjni_env); +} /* }}} void cjni_jvm_env_destroy */ + +/* Register ``native'' functions with the JVM. Native functions are C-functions + * that can be called by Java code. */ +static int cjni_init_native (JNIEnv *jvm_env) /* {{{ */ +{ + jclass api_class_ptr; + int status; + + api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org/collectd/api/Collectd"); + if (api_class_ptr == NULL) + { + ERROR ("cjni_init_native: Cannot find API class `org/collectd/api/Collectd'."); + return (-1); + } + + status = (*jvm_env)->RegisterNatives (jvm_env, api_class_ptr, + jni_api_functions, (jint) jni_api_functions_num); + if (status != 0) + { + ERROR ("cjni_init_native: RegisterNatives failed with status %i.", status); + return (-1); + } + + return (0); +} /* }}} int cjni_init_native */ + +/* Create the JVM. This is called when the first thread tries to access the JVM + * via cjni_thread_attach. */ +static int cjni_create_jvm (void) /* {{{ */ +{ + JNIEnv *jvm_env; + JavaVMInitArgs vm_args; + JavaVMOption vm_options[jvm_argc]; + + int status; + size_t i; + + if (jvm != NULL) + return (0); + + status = pthread_key_create (&jvm_env_key, cjni_jvm_env_destroy); + if (status != 0) + { + ERROR ("java plugin: cjni_create_jvm: pthread_key_create failed " + "with status %i.", status); + return (-1); + } + + jvm_env = NULL; + + memset (&vm_args, 0, sizeof (vm_args)); + vm_args.version = JNI_VERSION_1_2; + vm_args.options = vm_options; + vm_args.nOptions = (jint) jvm_argc; + + for (i = 0; i < jvm_argc; i++) + { + DEBUG ("java plugin: cjni_create_jvm: jvm_argv[%zu] = %s", + i, jvm_argv[i]); + vm_args.options[i].optionString = jvm_argv[i]; + } + + status = JNI_CreateJavaVM (&jvm, (void *) &jvm_env, (void *) &vm_args); + if (status != 0) + { + ERROR ("java plugin: cjni_create_jvm: " + "JNI_CreateJavaVM failed with status %i.", + status); + return (-1); + } + assert (jvm != NULL); + assert (jvm_env != NULL); + + /* Call RegisterNatives */ + status = cjni_init_native (jvm_env); + if (status != 0) + { + ERROR ("java plugin: cjni_create_jvm: cjni_init_native failed."); + return (-1); + } + + DEBUG ("java plugin: The JVM has been created."); + return (0); +} /* }}} int cjni_create_jvm */ + +/* Increase the reference counter to the JVM for this thread. If it was zero, + * attach the JVM first. */ +static JNIEnv *cjni_thread_attach (void) /* {{{ */ +{ + cjni_jvm_env_t *cjni_env; + JNIEnv *jvm_env; + + /* If we're the first thread to access the JVM, we'll have to create it + * first.. */ + if (jvm == NULL) + { + int status; + + status = cjni_create_jvm (); + if (status != 0) + { + ERROR ("java plugin: cjni_thread_attach: cjni_create_jvm failed."); + return (NULL); + } + } + assert (jvm != NULL); + + cjni_env = pthread_getspecific (jvm_env_key); + if (cjni_env == NULL) + { + /* This pointer is free'd in `cjni_jvm_env_destroy'. */ + cjni_env = (cjni_jvm_env_t *) malloc (sizeof (*cjni_env)); + if (cjni_env == NULL) + { + ERROR ("java plugin: cjni_thread_attach: malloc failed."); + return (NULL); + } + memset (cjni_env, 0, sizeof (*cjni_env)); + cjni_env->reference_counter = 0; + cjni_env->jvm_env = NULL; + + pthread_setspecific (jvm_env_key, cjni_env); + } + + if (cjni_env->reference_counter > 0) + { + cjni_env->reference_counter++; + jvm_env = cjni_env->jvm_env; + } + else + { + int status; + JavaVMAttachArgs args; + + assert (cjni_env->jvm_env == NULL); + + memset (&args, 0, sizeof (args)); + args.version = JNI_VERSION_1_2; + + status = (*jvm)->AttachCurrentThread (jvm, (void *) &jvm_env, (void *) &args); + if (status != 0) + { + ERROR ("java plugin: cjni_thread_attach: AttachCurrentThread failed " + "with status %i.", status); + return (NULL); + } + + cjni_env->reference_counter = 1; + cjni_env->jvm_env = jvm_env; + } + + DEBUG ("java plugin: cjni_thread_attach: cjni_env->reference_counter = %i", + cjni_env->reference_counter); + assert (jvm_env != NULL); + return (jvm_env); +} /* }}} JNIEnv *cjni_thread_attach */ + +/* Decrease the reference counter of this thread. If it reaches zero, detach + * from the JVM. */ +static int cjni_thread_detach (void) /* {{{ */ +{ + cjni_jvm_env_t *cjni_env; + int status; + + cjni_env = pthread_getspecific (jvm_env_key); + if (cjni_env == NULL) + { + ERROR ("java plugin: cjni_thread_detach: pthread_getspecific failed."); + return (-1); + } + + assert (cjni_env->reference_counter > 0); + assert (cjni_env->jvm_env != NULL); + + cjni_env->reference_counter--; + DEBUG ("java plugin: cjni_thread_detach: cjni_env->reference_counter = %i", + cjni_env->reference_counter); + + if (cjni_env->reference_counter > 0) + return (0); + + status = (*jvm)->DetachCurrentThread (jvm); + if (status != 0) + { + ERROR ("java plugin: cjni_thread_detach: DetachCurrentThread failed " + "with status %i.", status); + } + + cjni_env->reference_counter = 0; + cjni_env->jvm_env = NULL; + + return (0); +} /* }}} JNIEnv *cjni_thread_attach */ + +static int cjni_config_add_jvm_arg (oconfig_item_t *ci) /* {{{ */ +{ + char **tmp; + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("java plugin: `JVMArg' needs exactly one string argument."); + return (-1); + } + + if (jvm != NULL) + { + ERROR ("java plugin: All `JVMArg' options MUST appear before all " + "`LoadPlugin' options! The JVM is already started and I have to " + "ignore this argument: %s", + ci->values[0].value.string); + return (-1); + } + + tmp = (char **) realloc (jvm_argv, sizeof (char *) * (jvm_argc + 1)); + if (tmp == NULL) + { + ERROR ("java plugin: realloc failed."); + return (-1); + } + jvm_argv = tmp; + + jvm_argv[jvm_argc] = strdup (ci->values[0].value.string); + if (jvm_argv[jvm_argc] == NULL) + { + ERROR ("java plugin: strdup failed."); + return (-1); + } + jvm_argc++; + + return (0); +} /* }}} int cjni_config_add_jvm_arg */ + +static int cjni_config_load_plugin (oconfig_item_t *ci) /* {{{ */ +{ + JNIEnv *jvm_env; + java_plugin_class_t *class; + jmethodID constructor_id; + jobject tmp_object; + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("java plugin: `LoadPlugin' needs exactly one string argument."); + return (-1); + } + + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return (-1); + + class = (java_plugin_class_t *) realloc (java_classes_list, + (java_classes_list_len + 1) * sizeof (*java_classes_list)); + if (class == NULL) + { + ERROR ("java plugin: realloc failed."); + cjni_thread_detach (); + return (-1); + } + java_classes_list = class; + class = java_classes_list + java_classes_list_len; + + memset (class, 0, sizeof (*class)); + class->name = strdup (ci->values[0].value.string); + if (class->name == NULL) + { + ERROR ("java plugin: strdup failed."); + cjni_thread_detach (); + return (-1); + } + class->class = NULL; + class->object = NULL; + + DEBUG ("java plugin: Loading class %s", class->name); + + class->class = (*jvm_env)->FindClass (jvm_env, class->name); + if (class->class == NULL) + { + ERROR ("java plugin: cjni_config_load_plugin: FindClass (%s) failed.", + class->name); + cjni_thread_detach (); + free (class->name); + return (-1); + } + + constructor_id = (*jvm_env)->GetMethodID (jvm_env, class->class, + "", "()V"); + if (constructor_id == NULL) + { + ERROR ("java plugin: cjni_config_load_plugin: " + "Could not find the constructor for `%s'.", + class->name); + cjni_thread_detach (); + free (class->name); + return (-1); + } + + tmp_object = (*jvm_env)->NewObject (jvm_env, class->class, + constructor_id); + if (tmp_object != NULL) + class->object = (*jvm_env)->NewGlobalRef (jvm_env, tmp_object); + else + class->object = NULL; + if (class->object == NULL) + { + ERROR ("java plugin: cjni_config_load_plugin: " + "Could create a new `%s' object.", + class->name); + cjni_thread_detach (); + free (class->name); + return (-1); + } + + cjni_thread_detach (); + + java_classes_list_len++; + + return (0); +} /* }}} int cjni_config_load_plugin */ + +static int cjni_config_plugin_block (oconfig_item_t *ci) /* {{{ */ +{ + JNIEnv *jvm_env; + cjni_callback_info_t *cbi; + jobject o_ocitem; + const char *name; + int status; + size_t i; + + jclass class; + jmethodID method; + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("java plugin: `Plugin' blocks " + "need exactly one string argument."); + return (-1); + } + + name = ci->values[0].value.string; + + cbi = NULL; + for (i = 0; i < java_callbacks_num; i++) + { + if (java_callbacks[i].type != CB_TYPE_CONFIG) + continue; + + if (strcmp (name, java_callbacks[i].name) != 0) + continue; + + cbi = java_callbacks + i; + break; + } + + if (cbi == NULL) + { + NOTICE ("java plugin: Configuration block for `%s' found, but no such " + "configuration callback has been registered. Please make sure, the " + "`LoadPlugin' lines precede the `Plugin' blocks.", + name); + return (0); + } + + DEBUG ("java plugin: Configuring %s", name); + + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return (-1); + + o_ocitem = ctoj_oconfig_item (jvm_env, ci); + if (o_ocitem == NULL) + { + ERROR ("java plugin: cjni_config_plugin_block: ctoj_oconfig_item failed."); + cjni_thread_detach (); + return (-1); + } + + class = (*jvm_env)->GetObjectClass (jvm_env, cbi->object); + method = (*jvm_env)->GetMethodID (jvm_env, class, + "config", "(Lorg/collectd/api/OConfigItem;)I"); + + status = (*jvm_env)->CallIntMethod (jvm_env, + cbi->object, method, o_ocitem); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem); + cjni_thread_detach (); + return (0); +} /* }}} int cjni_config_plugin_block */ + +static int cjni_config (oconfig_item_t *ci) /* {{{ */ +{ + int success; + int errors; + int status; + int i; + + success = 0; + errors = 0; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("JVMArg", child->key) == 0) + { + status = cjni_config_add_jvm_arg (child); + if (status == 0) + success++; + else + errors++; + } + else if (strcasecmp ("LoadPlugin", child->key) == 0) + { + status = cjni_config_load_plugin (child); + if (status == 0) + success++; + else + errors++; + } + else if (strcasecmp ("Plugin", child->key) == 0) + { + status = cjni_config_plugin_block (child); + if (status == 0) + success++; + else + errors++; + } + else + { + WARNING ("java plugin: Option `%s' not allowed here.", child->key); + errors++; + } + } + + DEBUG ("java plugin: jvm_argc = %zu;", jvm_argc); + DEBUG ("java plugin: java_classes_list_len = %zu;", java_classes_list_len); + + if ((success == 0) && (errors > 0)) + { + ERROR ("java plugin: All statements failed."); + return (-1); + } + + return (0); +} /* }}} int cjni_config */ + +/* Free the data contained in the `user_data_t' pointer passed to `cjni_read' + * and `cjni_write'. In particular, delete the global reference to the Java + * object. */ +static void cjni_callback_info_destroy (void *arg) /* {{{ */ +{ + JNIEnv *jvm_env; + cjni_callback_info_t *cbi; + + DEBUG ("java plugin: cjni_callback_info_destroy (arg = %p);", arg); + + cbi = (cjni_callback_info_t *) arg; + + /* This condition can occurr when shutting down. */ + if (jvm == NULL) + { + sfree (cbi); + return; + } + + if (arg == NULL) + return; + + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + { + ERROR ("java plugin: cjni_callback_info_destroy: cjni_thread_attach failed."); + return; + } + + (*jvm_env)->DeleteGlobalRef (jvm_env, cbi->object); + + cbi->method = NULL; + cbi->object = NULL; + cbi->class = NULL; + free (cbi); + + cjni_thread_detach (); +} /* }}} void cjni_callback_info_destroy */ + +/* Call the CB_TYPE_READ callback pointed to by the `user_data_t' pointer. */ +static int cjni_read (user_data_t *ud) /* {{{ */ +{ + JNIEnv *jvm_env; + cjni_callback_info_t *cbi; + int status; + int ret_status; + + if (jvm == NULL) + { + ERROR ("java plugin: cjni_read: jvm == NULL"); + return (-1); + } + + if ((ud == NULL) || (ud->data == NULL)) + { + ERROR ("java plugin: cjni_read: Invalid user data."); + return (-1); + } + + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return (-1); + + cbi = (cjni_callback_info_t *) ud->data; + + ret_status = (*jvm_env)->CallIntMethod (jvm_env, cbi->object, + cbi->method); + + status = cjni_thread_detach (); + if (status != 0) + { + ERROR ("java plugin: cjni_read: cjni_thread_detach failed."); + return (-1); + } + + return (ret_status); +} /* }}} int cjni_read */ + +/* Call the CB_TYPE_WRITE callback pointed to by the `user_data_t' pointer. */ +static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */ + user_data_t *ud) +{ + JNIEnv *jvm_env; + cjni_callback_info_t *cbi; + jobject vl_java; + int status; + int ret_status; + + if (jvm == NULL) + { + ERROR ("java plugin: cjni_write: jvm == NULL"); + return (-1); + } + + if ((ud == NULL) || (ud->data == NULL)) + { + ERROR ("java plugin: cjni_write: Invalid user data."); + return (-1); + } + + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return (-1); + + cbi = (cjni_callback_info_t *) ud->data; + + vl_java = ctoj_value_list (jvm_env, ds, vl); + if (vl_java == NULL) + { + ERROR ("java plugin: cjni_write: ctoj_value_list failed."); + return (-1); + } + + ret_status = (*jvm_env)->CallIntMethod (jvm_env, + cbi->object, cbi->method, vl_java); + + (*jvm_env)->DeleteLocalRef (jvm_env, vl_java); + + status = cjni_thread_detach (); + if (status != 0) + { + ERROR ("java plugin: cjni_write: cjni_thread_detach failed."); + return (-1); + } + + return (ret_status); +} /* }}} int cjni_write */ + +/* Call the CB_TYPE_FLUSH callback pointed to by the `user_data_t' pointer. */ +static int cjni_flush (int timeout, const char *identifier, /* {{{ */ + user_data_t *ud) +{ + JNIEnv *jvm_env; + cjni_callback_info_t *cbi; + jobject o_identifier; + int status; + int ret_status; + + if (jvm == NULL) + { + ERROR ("java plugin: cjni_flush: jvm == NULL"); + return (-1); + } + + if ((ud == NULL) || (ud->data == NULL)) + { + ERROR ("java plugin: cjni_flush: Invalid user data."); + return (-1); + } + + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return (-1); + + cbi = (cjni_callback_info_t *) ud->data; + + o_identifier = NULL; + if (identifier != NULL) + { + o_identifier = (*jvm_env)->NewStringUTF (jvm_env, identifier); + if (o_identifier == NULL) + { + ERROR ("java plugin: cjni_flush: NewStringUTF failed."); + return (-1); + } + } + + ret_status = (*jvm_env)->CallIntMethod (jvm_env, + cbi->object, cbi->method, (jint) timeout, o_identifier); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_identifier); + + status = cjni_thread_detach (); + if (status != 0) + { + ERROR ("java plugin: cjni_flush: cjni_thread_detach failed."); + return (-1); + } + + return (ret_status); +} /* }}} int cjni_flush */ + +/* Call the CB_TYPE_LOG callback pointed to by the `user_data_t' pointer. */ +static void cjni_log (int severity, const char *message, /* {{{ */ + user_data_t *ud) +{ + JNIEnv *jvm_env; + cjni_callback_info_t *cbi; + jobject o_message; + + if (jvm == NULL) + return; + + if ((ud == NULL) || (ud->data == NULL)) + return; + + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return; + + cbi = (cjni_callback_info_t *) ud->data; + + o_message = (*jvm_env)->NewStringUTF (jvm_env, message); + if (o_message == NULL) + return; + + (*jvm_env)->CallVoidMethod (jvm_env, + cbi->object, cbi->method, (jint) severity, o_message); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_message); + + cjni_thread_detach (); +} /* }}} void cjni_log */ + +/* Call the CB_TYPE_NOTIFICATION callback pointed to by the `user_data_t' + * pointer. */ +static int cjni_notification (const notification_t *n, /* {{{ */ + user_data_t *ud) +{ + JNIEnv *jvm_env; + cjni_callback_info_t *cbi; + jobject o_notification; + int status; + int ret_status; + + if (jvm == NULL) + { + ERROR ("java plugin: cjni_read: jvm == NULL"); + return (-1); + } + + if ((ud == NULL) || (ud->data == NULL)) + { + ERROR ("java plugin: cjni_read: Invalid user data."); + return (-1); + } + + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return (-1); + + cbi = (cjni_callback_info_t *) ud->data; + + o_notification = ctoj_notification (jvm_env, n); + if (o_notification == NULL) + { + ERROR ("java plugin: cjni_notification: ctoj_notification failed."); + return (-1); + } + + ret_status = (*jvm_env)->CallIntMethod (jvm_env, + cbi->object, cbi->method, o_notification); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_notification); + + status = cjni_thread_detach (); + if (status != 0) + { + ERROR ("java plugin: cjni_read: cjni_thread_detach failed."); + return (-1); + } + + return (ret_status); +} /* }}} int cjni_notification */ + +/* Callbacks for matches implemented in Java */ +static int cjni_match_target_create (const oconfig_item_t *ci, /* {{{ */ + void **user_data) +{ + JNIEnv *jvm_env; + cjni_callback_info_t *cbi_ret; + cjni_callback_info_t *cbi_factory; + const char *name; + jobject o_ci; + jobject o_tmp; + int type; + size_t i; + + cbi_ret = NULL; + o_ci = NULL; + jvm_env = NULL; + +#define BAIL_OUT(status) \ + if (cbi_ret != NULL) { \ + free (cbi_ret->name); \ + if ((jvm_env != NULL) && (cbi_ret->object != NULL)) \ + (*jvm_env)->DeleteLocalRef (jvm_env, cbi_ret->object); \ + } \ + free (cbi_ret); \ + if (jvm_env != NULL) { \ + if (o_ci != NULL) \ + (*jvm_env)->DeleteLocalRef (jvm_env, o_ci); \ + cjni_thread_detach (); \ + } \ + return (status) + + if (jvm == NULL) + { + ERROR ("java plugin: cjni_read: jvm == NULL"); + BAIL_OUT (-1); + } + + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + { + BAIL_OUT (-1); + } + + /* Find out whether to create a match or a target. */ + if (strcasecmp ("Match", ci->key) == 0) + type = CB_TYPE_MATCH; + else if (strcasecmp ("Target", ci->key) == 0) + type = CB_TYPE_TARGET; + else + { + ERROR ("java plugin: cjni_match_target_create: Can't figure out whether " + "to create a match or a target."); + BAIL_OUT (-1); + } + + /* This is the name of the match we should create. */ + name = ci->values[0].value.string; + + /* Lets see if we have a matching factory here.. */ + cbi_factory = NULL; + for (i = 0; i < java_callbacks_num; i++) + { + if (java_callbacks[i].type != type) + continue; + + if (strcmp (name, java_callbacks[i].name) != 0) + continue; + + cbi_factory = java_callbacks + i; + break; + } + + /* Nope, no factory for that name.. */ + if (cbi_factory == NULL) + { + ERROR ("java plugin: cjni_match_target_create: " + "No such match factory registered: %s", + name); + BAIL_OUT (-1); + } + + /* We convert `ci' to its Java equivalent.. */ + o_ci = ctoj_oconfig_item (jvm_env, ci); + if (o_ci == NULL) + { + ERROR ("java plugin: cjni_match_target_create: " + "ctoj_oconfig_item failed."); + BAIL_OUT (-1); + } + + /* Allocate a new callback info structure. This is going to be our user_data + * pointer. */ + cbi_ret = (cjni_callback_info_t *) malloc (sizeof (*cbi_ret)); + if (cbi_ret == NULL) + { + ERROR ("java plugin: cjni_match_target_create: malloc failed."); + BAIL_OUT (-1); + } + memset (cbi_ret, 0, sizeof (*cbi_ret)); + cbi_ret->object = NULL; + cbi_ret->type = type; + + /* Lets fill the callback info structure.. First, the name: */ + cbi_ret->name = strdup (name); + if (cbi_ret->name == NULL) + { + ERROR ("java plugin: cjni_match_target_create: strdup failed."); + BAIL_OUT (-1); + } + + /* Then call the factory method so it creates a new object for us. */ + o_tmp = (*jvm_env)->CallObjectMethod (jvm_env, + cbi_factory->object, cbi_factory->method, o_ci); + if (o_tmp == NULL) + { + ERROR ("java plugin: cjni_match_target_create: CallObjectMethod failed."); + BAIL_OUT (-1); + } + + cbi_ret->object = (*jvm_env)->NewGlobalRef (jvm_env, o_tmp); + if (o_tmp == NULL) + { + ERROR ("java plugin: cjni_match_target_create: NewGlobalRef failed."); + BAIL_OUT (-1); + } + + /* This is the class of the match. It is possibly different from the class of + * the match-factory! */ + cbi_ret->class = (*jvm_env)->GetObjectClass (jvm_env, cbi_ret->object); + if (cbi_ret->class == NULL) + { + ERROR ("java plugin: cjni_match_target_create: GetObjectClass failed."); + BAIL_OUT (-1); + } + + /* Lookup the `int match (DataSet, ValueList)' method. */ + cbi_ret->method = (*jvm_env)->GetMethodID (jvm_env, cbi_ret->class, + /* method name = */ (type == CB_TYPE_MATCH) ? "match" : "invoke", + "(Lorg/collectd/api/DataSet;Lorg/collectd/api/ValueList;)I"); + if (cbi_ret->method == NULL) + { + ERROR ("java plugin: cjni_match_target_create: GetMethodID failed."); + BAIL_OUT (-1); + } + + /* Return the newly created match via the user_data pointer. */ + *user_data = (void *) cbi_ret; + + cjni_thread_detach (); + + DEBUG ("java plugin: cjni_match_target_create: " + "Successfully created a `%s' %s.", + cbi_ret->name, (type == CB_TYPE_MATCH) ? "match" : "target"); + + /* Success! */ + return (0); +#undef BAIL_OUT +} /* }}} int cjni_match_target_create */ + +static int cjni_match_target_destroy (void **user_data) /* {{{ */ +{ + cjni_callback_info_destroy (*user_data); + *user_data = NULL; + + return (0); +} /* }}} int cjni_match_target_destroy */ + +static int cjni_match_target_invoke (const data_set_t *ds, /* {{{ */ + value_list_t *vl, notification_meta_t **meta, void **user_data) +{ + JNIEnv *jvm_env; + cjni_callback_info_t *cbi; + jobject o_vl; + jobject o_ds; + int ret_status; + int status; + + if (jvm == NULL) + { + ERROR ("java plugin: cjni_match_target_invoke: jvm == NULL"); + return (-1); + } + + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return (-1); + + cbi = (cjni_callback_info_t *) *user_data; + + o_vl = ctoj_value_list (jvm_env, ds, vl); + if (o_vl == NULL) + { + ERROR ("java plugin: cjni_match_target_invoke: ctoj_value_list failed."); + cjni_thread_detach (); + return (-1); + } + + o_ds = ctoj_data_set (jvm_env, ds); + if (o_ds == NULL) + { + ERROR ("java plugin: cjni_match_target_invoke: ctoj_value_list failed."); + cjni_thread_detach (); + return (-1); + } + + ret_status = (*jvm_env)->CallIntMethod (jvm_env, cbi->object, cbi->method, + o_ds, o_vl); + + DEBUG ("java plugin: cjni_match_target_invoke: Method returned %i.", ret_status); + + /* If we're executing a target, copy the `ValueList' back to our + * `value_list_t'. */ + if (cbi->type == CB_TYPE_TARGET) + { + value_list_t new_vl; + + memset (&new_vl, 0, sizeof (new_vl)); + status = jtoc_value_list (jvm_env, &new_vl, o_vl); + if (status != 0) + { + ERROR ("java plugin: cjni_match_target_invoke: " + "jtoc_value_list failed."); + } + else /* if (status == 0) */ + { + /* plugin_dispatch_values assures that this is dynamically allocated + * memory. */ + sfree (vl->values); + + /* This will replace the vl->values pointer to a new, dynamically + * allocated piece of memory. */ + memcpy (vl, &new_vl, sizeof (*vl)); + } + } /* if (cbi->type == CB_TYPE_TARGET) */ + + status = cjni_thread_detach (); + if (status != 0) + ERROR ("java plugin: cjni_read: cjni_thread_detach failed."); + + return (ret_status); +} /* }}} int cjni_match_target_invoke */ + +/* Iterate over `java_callbacks' and call all CB_TYPE_INIT callbacks. */ +static int cjni_init_plugins (JNIEnv *jvm_env) /* {{{ */ +{ + int status; + size_t i; + + for (i = 0; i < java_callbacks_num; i++) + { + if (java_callbacks[i].type != CB_TYPE_INIT) + continue; + + DEBUG ("java plugin: Initializing %s", java_callbacks[i].name); + + status = (*jvm_env)->CallIntMethod (jvm_env, + java_callbacks[i].object, java_callbacks[i].method); + if (status != 0) + { + ERROR ("java plugin: Initializing `%s' failed with status %i. " + "Removing read function.", + java_callbacks[i].name, status); + plugin_unregister_read (java_callbacks[i].name); + } + } + + return (0); +} /* }}} int cjni_init_plugins */ + +/* Iterate over `java_callbacks' and call all CB_TYPE_SHUTDOWN callbacks. */ +static int cjni_shutdown_plugins (JNIEnv *jvm_env) /* {{{ */ +{ + int status; + size_t i; + + for (i = 0; i < java_callbacks_num; i++) + { + if (java_callbacks[i].type != CB_TYPE_SHUTDOWN) + continue; + + DEBUG ("java plugin: Shutting down %s", java_callbacks[i].name); + + status = (*jvm_env)->CallIntMethod (jvm_env, + java_callbacks[i].object, java_callbacks[i].method); + if (status != 0) + { + ERROR ("java plugin: Shutting down `%s' failed with status %i. ", + java_callbacks[i].name, status); + } + } + + return (0); +} /* }}} int cjni_shutdown_plugins */ + + +static int cjni_shutdown (void) /* {{{ */ +{ + JNIEnv *jvm_env; + JavaVMAttachArgs args; + int status; + size_t i; + + if (jvm == NULL) + return (0); + + jvm_env = NULL; + memset (&args, 0, sizeof (args)); + args.version = JNI_VERSION_1_2; + + status = (*jvm)->AttachCurrentThread (jvm, (void *) &jvm_env, &args); + if (status != 0) + { + ERROR ("java plugin: cjni_shutdown: AttachCurrentThread failed with status %i.", + status); + return (-1); + } + + /* Execute all the shutdown functions registered by plugins. */ + cjni_shutdown_plugins (jvm_env); + + /* Release all the global references to callback functions */ + for (i = 0; i < java_callbacks_num; i++) + { + if (java_callbacks[i].object != NULL) + { + (*jvm_env)->DeleteGlobalRef (jvm_env, java_callbacks[i].object); + java_callbacks[i].object = NULL; + } + sfree (java_callbacks[i].name); + } + java_callbacks_num = 0; + sfree (java_callbacks); + + /* Release all the global references to directly loaded classes. */ + for (i = 0; i < java_classes_list_len; i++) + { + if (java_classes_list[i].object != NULL) + { + (*jvm_env)->DeleteGlobalRef (jvm_env, java_classes_list[i].object); + java_classes_list[i].object = NULL; + } + sfree (java_classes_list[i].name); + } + java_classes_list_len = 0; + sfree (java_classes_list); + + /* Destroy the JVM */ + DEBUG ("java plugin: Destroying the JVM."); + (*jvm)->DestroyJavaVM (jvm); + jvm = NULL; + jvm_env = NULL; + + pthread_key_delete (jvm_env_key); + + /* Free the JVM argument list */ + for (i = 0; i < jvm_argc; i++) + sfree (jvm_argv[i]); + jvm_argc = 0; + sfree (jvm_argv); + + return (0); +} /* }}} int cjni_shutdown */ + +/* Initialization: Create a JVM, load all configured classes and call their + * `config' and `init' callback methods. */ +static int cjni_init (void) /* {{{ */ +{ + JNIEnv *jvm_env; + + if (jvm == NULL) + { + ERROR ("java plugin: cjni_init: jvm == NULL"); + return (-1); + } + + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return (-1); + + cjni_init_plugins (jvm_env); + + cjni_thread_detach (); + return (0); +} /* }}} int cjni_init */ + +void module_register (void) +{ + plugin_register_complex_config ("java", cjni_config); + plugin_register_init ("java", cjni_init); + plugin_register_shutdown ("java", cjni_shutdown); +} /* void module_register (void) */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/liboconfig/oconfig.c b/src/liboconfig/oconfig.c index 79b53aec..629775ab 100644 --- a/src/liboconfig/oconfig.c +++ b/src/liboconfig/oconfig.c @@ -95,6 +95,98 @@ oconfig_item_t *oconfig_parse_file (const char *file) return (ret); } /* oconfig_item_t *oconfig_parse_file */ +oconfig_item_t *oconfig_clone (const oconfig_item_t *ci_orig) +{ + oconfig_item_t *ci_copy; + + ci_copy = (oconfig_item_t *) malloc (sizeof (*ci_copy)); + if (ci_copy == NULL) + { + fprintf (stderr, "malloc failed.\n"); + return (NULL); + } + memset (ci_copy, 0, sizeof (*ci_copy)); + ci_copy->values = NULL; + ci_copy->parent = NULL; + ci_copy->children = NULL; + + ci_copy->key = strdup (ci_orig->key); + if (ci_copy->key == NULL) + { + fprintf (stderr, "strdup failed.\n"); + free (ci_copy); + return (NULL); + } + + if (ci_orig->values_num > 0) /* {{{ */ + { + int i; + + ci_copy->values = (oconfig_value_t *) calloc (ci_orig->values_num, + sizeof (*ci_copy->values)); + if (ci_copy->values == NULL) + { + fprintf (stderr, "calloc failed.\n"); + free (ci_copy->key); + free (ci_copy); + return (NULL); + } + ci_copy->values_num = ci_orig->values_num; + + for (i = 0; i < ci_copy->values_num; i++) + { + ci_copy->values[i].type = ci_orig->values[i].type; + if (ci_copy->values[i].type == OCONFIG_TYPE_STRING) + { + ci_copy->values[i].value.string + = strdup (ci_orig->values[i].value.string); + if (ci_copy->values[i].value.string == NULL) + { + fprintf (stderr, "strdup failed.\n"); + oconfig_free (ci_copy); + return (NULL); + } + } + else /* ci_copy->values[i].type != OCONFIG_TYPE_STRING) */ + { + ci_copy->values[i].value = ci_orig->values[i].value; + } + } + } /* }}} if (ci_orig->values_num > 0) */ + + if (ci_orig->children_num > 0) /* {{{ */ + { + int i; + + ci_copy->children = (oconfig_item_t *) calloc (ci_orig->children_num, + sizeof (*ci_copy->children)); + if (ci_copy->children == NULL) + { + fprintf (stderr, "calloc failed.\n"); + oconfig_free (ci_copy); + return (NULL); + } + ci_copy->children_num = ci_orig->children_num; + + for (i = 0; i < ci_copy->children_num; i++) + { + oconfig_item_t *child; + + child = oconfig_clone (ci_orig->children + i); + if (child == NULL) + { + oconfig_free (ci_copy); + return (NULL); + } + child->parent = ci_copy; + ci_copy->children[i] = *child; + free (child); + } /* for (i = 0; i < ci_copy->children_num; i++) */ + } /* }}} if (ci_orig->children_num > 0) */ + + return (ci_copy); +} /* oconfig_item_t *oconfig_clone */ + void oconfig_free (oconfig_item_t *ci) { int i; @@ -121,5 +213,5 @@ void oconfig_free (oconfig_item_t *ci) } /* - * vim:shiftwidth=2:tabstop=8:softtabstop=2 + * vim:shiftwidth=2:tabstop=8:softtabstop=2:fdm=marker */ diff --git a/src/liboconfig/oconfig.h b/src/liboconfig/oconfig.h index e6e53e27..70fc6230 100644 --- a/src/liboconfig/oconfig.h +++ b/src/liboconfig/oconfig.h @@ -5,7 +5,7 @@ /** * oconfig - src/oconfig.h - * Copyright (C) 2006,2007 Florian octo Forster + * Copyright (C) 2006-2009 Florian octo Forster * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -59,6 +59,8 @@ struct oconfig_item_s oconfig_item_t *oconfig_parse_fh (FILE *fh); oconfig_item_t *oconfig_parse_file (const char *file); +oconfig_item_t *oconfig_clone (const oconfig_item_t *ci); + void oconfig_free (oconfig_item_t *ci); /* diff --git a/src/liboping/AUTHORS b/src/liboping/AUTHORS deleted file mode 100644 index e69de29b..00000000 diff --git a/src/liboping/COPYING b/src/liboping/COPYING deleted file mode 100644 index 623b6258..00000000 --- a/src/liboping/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/src/liboping/ChangeLog b/src/liboping/ChangeLog deleted file mode 100644 index c4bd92d9..00000000 --- a/src/liboping/ChangeLog +++ /dev/null @@ -1,31 +0,0 @@ -2006-06-05, Version 0.2.2 - * The `oping' application didn't exit if no hosts could be resolved. - This release fixes it's behavior. - -2006-06-01, Version 0.2.1 - * Fix the behavior for non GNU-Linux systems. liboping tried to - `bind(2)' to the raw-socket it uses to send ICMP packets. Apparently - (decided by majority vote ;) this is not the right thing to do. - GNU/Linux never complained about it, but works find without the bind. - Other operating systems don't work at all with the bind. - * Build fixes for non-GNU/Linux platforms: Mac OS X doesn't define - `size_t' as `unsigned int' and therefore needs casting and FreeBSD - needs to have `sys/types.h' included before `netinet/*.h' - -2006-05-29, Version 0.2.0 - * It's now possible to set the data to be send to the remote host and - to get the data received from the host. - * The `oping' binary now calculates the standard deviation. Also, it - displays the number of byes that were received and other output - changes. - * Hosts are now returned in the same order as they were added by - `ping_host_add'. This is not guaranteed, but makes `oping' prettier. - -2006-05-12, Version 0.1.1 - * A bug in the library has been fixed: When the sequence got higher - than 2^16 the counter in the packets wrapped around, but the - internal counter didn't, causing the library to ignore all further - ICMP packets. This affected both, ICMPv4 and ICMPv6. - -2006-05-08, Version 0.1.0 - * Initial release. diff --git a/src/liboping/Makefile.am b/src/liboping/Makefile.am deleted file mode 100644 index 1b004105..00000000 --- a/src/liboping/Makefile.am +++ /dev/null @@ -1,16 +0,0 @@ -AUTOMAKE_OPTIONS = foreign no-dependencies - -if COMPILER_IS_GCC -AM_CFLAGS = -Wall -Werror -endif - -EXTRA_DIST = AUTHORS COPYING ChangeLog README - -noinst_LTLIBRARIES = liboping.la - -#liboping_la_CFLAGS = -liboping_la_LDFLAGS = -version-info 0:3:0 -if BUILD_WITH_LIBSOCKET -liboping_la_LDFLAGS += -lsocket -endif -liboping_la_SOURCES = liboping.c oping.h diff --git a/src/liboping/README b/src/liboping/README deleted file mode 100644 index 7d2e8d46..00000000 --- a/src/liboping/README +++ /dev/null @@ -1,28 +0,0 @@ - liboping - Library to ping IPv4 and IPv6 hosts in parallel -============================================================ -http://verplant.org/liboping/ - -About ------ - - liboping was inspired by ping, libping and fping: It differs from these - existing solutions in that it can `ping' multiple hosts in parallel using - IPv4 or IPv6 transparently. Other design principles were an object oriented - interface, simplicity and extensibility. - - -Features --------- - - * Support for multiple hosts. - - * Support for IPv4 and IPv6. - - * Object oriented interface. - - -Author ------- - - Florian octo Forster - diff --git a/src/liboping/liboping.c b/src/liboping/liboping.c deleted file mode 100644 index fb3f8431..00000000 --- a/src/liboping/liboping.c +++ /dev/null @@ -1,1220 +0,0 @@ -/** - * Object oriented C module to send ICMP and ICMPv6 `echo's. - * Copyright (C) 2006 Florian octo Forster - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#if HAVE_CONFIG_H -# include -#endif - -#if STDC_HEADERS -# include -# include -# include -# include -# include -#else -# error "You don't have the standard C99 header files installed" -#endif /* STDC_HEADERS */ - -#if HAVE_UNISTD_H -# include -#endif - -#if HAVE_FCNTL_H -# include -#endif -#if HAVE_SYS_TYPES_H -# include -#endif -#if HAVE_SYS_STAT_H -# include -#endif - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - -#if HAVE_SYS_SOCKET_H -# include -#endif -#if HAVE_NETDB_H -# include -#endif - -#if HAVE_NETINET_IN_SYSTM_H -# include -#endif -#if HAVE_NETINET_IN_H -# include -#endif -#if HAVE_NETINET_IP_H -# include -#endif -#if HAVE_NETINET_IP_ICMP_H -# include -#endif -#ifdef HAVE_NETINET_IP_VAR_H -# include -#endif -#if HAVE_NETINET_IP6_H -# include -#endif -#if HAVE_NETINET_ICMP6_H -# include -#endif - -#include "oping.h" - -#if WITH_DEBUG -# define dprintf(...) printf ("%s[%4i]: %-20s: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__) -#else -# define dprintf(...) /**/ -#endif - -#define PING_ERRMSG_LEN 256 - -struct pinghost -{ - char *hostname; - struct sockaddr_storage *addr; - socklen_t addrlen; - int addrfamily; - int fd; - int ident; - int sequence; - struct timeval *timer; - double latency; - char *data; - - void *context; - - struct pinghost *next; -}; - -struct pingobj -{ - double timeout; - int ttl; - int addrfamily; - char *data; - - char errmsg[PING_ERRMSG_LEN]; - - pinghost_t *head; -}; - -/* - * private (static) functions - */ -static void ping_set_error (pingobj_t *obj, const char *function, - const char *message) -{ - snprintf (obj->errmsg, PING_ERRMSG_LEN, "%s: %s", function, message); - obj->errmsg[PING_ERRMSG_LEN - 1] = '\0'; -} - -static int ping_timeval_add (struct timeval *tv1, struct timeval *tv2, - struct timeval *res) -{ - res->tv_sec = tv1->tv_sec + tv2->tv_sec; - res->tv_usec = tv1->tv_usec + tv2->tv_usec; - - while (res->tv_usec > 1000000) - { - res->tv_usec -= 1000000; - res->tv_sec++; - } - - return (0); -} - -static int ping_timeval_sub (struct timeval *tv1, struct timeval *tv2, - struct timeval *res) -{ - - if ((tv1->tv_sec < tv2->tv_sec) - || ((tv1->tv_sec == tv2->tv_sec) - && (tv1->tv_usec < tv2->tv_usec))) - return (-1); - - res->tv_sec = tv1->tv_sec - tv2->tv_sec; - res->tv_usec = tv1->tv_usec - tv2->tv_usec; - - assert ((res->tv_sec > 0) || ((res->tv_sec == 0) && (res->tv_usec > 0))); - - while (res->tv_usec < 0) - { - res->tv_usec += 1000000; - res->tv_sec--; - } - - return (0); -} - -static uint16_t ping_icmp4_checksum (char *buf, size_t len) -{ - uint32_t sum = 0; - uint16_t ret = 0; - - uint16_t *ptr; - - for (ptr = (uint16_t *) buf; len > 1; ptr++, len -= 2) - sum += *ptr; - - if (len == 1) - { - *(char *) &ret = *(char *) ptr; - sum += ret; - } - - /* Do this twice to get all possible carries.. */ - sum = (sum >> 16) + (sum & 0xFFFF); - sum = (sum >> 16) + (sum & 0xFFFF); - - ret = ~sum; - - return (ret); -} - -static pinghost_t *ping_receive_ipv4 (pinghost_t *ph, char *buffer, size_t buffer_len) -{ - struct ip *ip_hdr; - struct icmp *icmp_hdr; - - size_t ip_hdr_len; - - uint16_t recv_checksum; - uint16_t calc_checksum; - - uint16_t ident; - uint16_t seq; - - pinghost_t *ptr; - - if (buffer_len < sizeof (struct ip)) - return (NULL); - - ip_hdr = (struct ip *) buffer; - ip_hdr_len = ip_hdr->ip_hl << 2; - - if (buffer_len < ip_hdr_len) - return (NULL); - - buffer += ip_hdr_len; - buffer_len -= ip_hdr_len; - - if (buffer_len < sizeof (struct icmp)) - return (NULL); - - icmp_hdr = (struct icmp *) buffer; - buffer += sizeof (struct icmp); - buffer_len -= sizeof (struct icmp); - - if (icmp_hdr->icmp_type != ICMP_ECHOREPLY) - { - dprintf ("Unexpected ICMP type: %i\n", icmp_hdr->icmp_type); - return (NULL); - } - - recv_checksum = icmp_hdr->icmp_cksum; - icmp_hdr->icmp_cksum = 0; - calc_checksum = ping_icmp4_checksum ((char *) icmp_hdr, - sizeof (struct icmp) + buffer_len); - - if (recv_checksum != calc_checksum) - { - dprintf ("Checksum missmatch: Got 0x%04x, calculated 0x%04x\n", - recv_checksum, calc_checksum); - return (NULL); - } - - ident = ntohs (icmp_hdr->icmp_id); - seq = ntohs (icmp_hdr->icmp_seq); - - for (ptr = ph; ptr != NULL; ptr = ptr->next) - { - dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n", - ptr->hostname, ptr->ident, ((ptr->sequence - 1) & 0xFFFF)); - - if (ptr->addrfamily != AF_INET) - continue; - - if (!timerisset (ptr->timer)) - continue; - - if (ptr->ident != ident) - continue; - - if (((ptr->sequence - 1) & 0xFFFF) != seq) - continue; - - dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n", - ptr->hostname, ident, seq); - - break; - } - - if (ptr == NULL) - { - dprintf ("No match found for ident = 0x%04x, seq = %i\n", - ident, seq); - } - - return (ptr); -} - -static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffer_len) -{ - struct icmp6_hdr *icmp_hdr; - - uint16_t ident; - uint16_t seq; - - pinghost_t *ptr; - - if (buffer_len < sizeof (struct icmp6_hdr)) - return (NULL); - - icmp_hdr = (struct icmp6_hdr *) buffer; - buffer += sizeof (struct icmp); - buffer_len -= sizeof (struct icmp); - - if (icmp_hdr->icmp6_type != ICMP6_ECHO_REPLY) - { - dprintf ("Unexpected ICMP type: %02x\n", icmp_hdr->icmp6_type); - return (NULL); - } - - if (icmp_hdr->icmp6_code != 0) - { - dprintf ("Unexpected ICMP code: %02x\n", icmp_hdr->icmp6_code); - return (NULL); - } - - ident = ntohs (icmp_hdr->icmp6_id); - seq = ntohs (icmp_hdr->icmp6_seq); - - for (ptr = ph; ptr != NULL; ptr = ptr->next) - { - dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n", - ptr->hostname, ptr->ident, ((ptr->sequence - 1) & 0xFFFF)); - - if (ptr->addrfamily != AF_INET6) - continue; - - if (!timerisset (ptr->timer)) - continue; - - if (ptr->ident != ident) - continue; - - if (((ptr->sequence - 1) & 0xFFFF) != seq) - continue; - - dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n", - ptr->hostname, ident, seq); - - break; - } - - if (ptr == NULL) - { - dprintf ("No match found for ident = 0x%04x, seq = %i\n", - ident, seq); - } - - return (ptr); -} - -static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now) -{ - char buffer[4096]; - size_t buffer_len; - - struct timeval diff; - - pinghost_t *host = NULL; - - struct sockaddr_storage sa; - socklen_t sa_len; - - sa_len = sizeof (sa); - - buffer_len = recvfrom (fd, buffer, sizeof (buffer), 0, - (struct sockaddr *) &sa, &sa_len); - if (buffer_len == -1) - { - dprintf ("recvfrom: %s\n", strerror (errno)); - return (-1); - } - - dprintf ("Read %u bytes from fd = %i\n", (unsigned int) buffer_len, fd); - - if (sa.ss_family == AF_INET) - { - if ((host = ping_receive_ipv4 (ph, buffer, buffer_len)) == NULL) - return (-1); - } - else if (sa.ss_family == AF_INET6) - { - if ((host = ping_receive_ipv6 (ph, buffer, buffer_len)) == NULL) - return (-1); - } - - dprintf ("rcvd: %12i.%06i\n", - (int) now->tv_sec, - (int) now->tv_usec); - dprintf ("sent: %12i.%06i\n", - (int) host->timer->tv_sec, - (int) host->timer->tv_usec); - - if (ping_timeval_sub (now, host->timer, &diff) < 0) - { - timerclear (host->timer); - return (-1); - } - - dprintf ("diff: %12i.%06i\n", - (int) diff.tv_sec, - (int) diff.tv_usec); - - host->latency = ((double) diff.tv_usec) / 1000.0; - host->latency += ((double) diff.tv_sec) * 1000.0; - - timerclear (host->timer); - - return (0); -} - -static int ping_receive_all (pingobj_t *obj) -{ - fd_set readfds; - int num_readfds; - int max_readfds; - - pinghost_t *ph; - pinghost_t *ptr; - - struct timeval endtime; - struct timeval nowtime; - struct timeval timeout; - int status; - - int ret; - - ph = obj->head; - ret = 0; - - for (ptr = ph; ptr != NULL; ptr = ptr->next) - ptr->latency = -1.0; - - if (gettimeofday (&nowtime, NULL) == -1) - { - ping_set_error (obj, "gettimeofday", strerror (errno)); - return (-1); - } - - /* Set up timeout */ - timeout.tv_sec = (time_t) obj->timeout; - timeout.tv_usec = (suseconds_t) (1000000 * (obj->timeout - ((double) timeout.tv_sec))); - - dprintf ("Set timeout to %i.%06i seconds\n", - (int) timeout.tv_sec, - (int) timeout.tv_usec); - - ping_timeval_add (&nowtime, &timeout, &endtime); - - while (1) - { - FD_ZERO (&readfds); - num_readfds = 0; - max_readfds = -1; - - for (ptr = ph; ptr != NULL; ptr = ptr->next) - { - if (!timerisset (ptr->timer)) - continue; - - FD_SET (ptr->fd, &readfds); - num_readfds++; - - if (max_readfds < ptr->fd) - max_readfds = ptr->fd; - } - - if (num_readfds == 0) - break; - - if (gettimeofday (&nowtime, NULL) == -1) - { - ping_set_error (obj, "gettimeofday", strerror (errno)); - return (-1); - } - - if (ping_timeval_sub (&endtime, &nowtime, &timeout) == -1) - break; - - dprintf ("Waiting on %i sockets for %i.%06i seconds\n", num_readfds, - (int) timeout.tv_sec, - (int) timeout.tv_usec); - - status = select (max_readfds + 1, &readfds, NULL, NULL, &timeout); - - if (gettimeofday (&nowtime, NULL) == -1) - { - ping_set_error (obj, "gettimeofday", strerror (errno)); - return (-1); - } - - if ((status == -1) && (errno == EINTR)) - { - dprintf ("select was interrupted by signal..\n"); - continue; - } - else if (status < 0) - { - dprintf ("select: %s\n", strerror (errno)); - break; - } - else if (status == 0) - { - dprintf ("select timed out\n"); - break; - } - - for (ptr = ph; ptr != NULL; ptr = ptr->next) - { - if (FD_ISSET (ptr->fd, &readfds)) - if (ping_receive_one (ptr->fd, ph, &nowtime) == 0) - ret++; - } - } /* while (1) */ - - return (ret); -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Sending functions: * - * * - * ping_send_all * - * +-> ping_send_one_ipv4 * - * `-> ping_send_one_ipv6 * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -static ssize_t ping_sendto (pingobj_t *obj, pinghost_t *ph, - const void *buf, size_t buflen) -{ - ssize_t ret; - - if (gettimeofday (ph->timer, NULL) == -1) - { - timerclear (ph->timer); - return (-1); - } - - ret = sendto (ph->fd, buf, buflen, 0, - (struct sockaddr *) ph->addr, ph->addrlen); - - if (ret < 0) - { -#if defined(EHOSTUNREACH) - if (errno == EHOSTUNREACH) - return (0); -#endif -#if defined(ENETUNREACH) - if (errno == ENETUNREACH) - return (0); -#endif - ping_set_error (obj, "sendto", strerror (errno)); - } - - return (ret); -} - -static int ping_send_one_ipv4 (pingobj_t *obj, pinghost_t *ph) -{ - struct icmp *icmp4; - int status; - - char buf[4096]; - int buflen; - - char *data; - int datalen; - - dprintf ("ph->hostname = %s\n", ph->hostname); - - memset (buf, '\0', sizeof (buf)); - icmp4 = (struct icmp *) buf; - data = (char *) (icmp4 + 1); - - icmp4->icmp_type = ICMP_ECHO; - icmp4->icmp_code = 0; - icmp4->icmp_cksum = 0; - icmp4->icmp_id = htons (ph->ident); - icmp4->icmp_seq = htons (ph->sequence); - - buflen = 4096 - sizeof (struct icmp); - strncpy (data, ph->data, buflen); - datalen = strlen (data); - - buflen = datalen + sizeof (struct icmp); - - icmp4->icmp_cksum = ping_icmp4_checksum (buf, buflen); - - dprintf ("Sending ICMPv4 package with ID 0x%04x\n", ph->ident); - - status = ping_sendto (obj, ph, buf, buflen); - if (status < 0) - { - perror ("ping_sendto"); - return (-1); - } - - dprintf ("sendto: status = %i\n", status); - - return (0); -} - -static int ping_send_one_ipv6 (pingobj_t *obj, pinghost_t *ph) -{ - struct icmp6_hdr *icmp6; - int status; - - char buf[4096]; - int buflen; - - char *data; - int datalen; - - dprintf ("ph->hostname = %s\n", ph->hostname); - - memset (buf, '\0', sizeof (buf)); - icmp6 = (struct icmp6_hdr *) buf; - data = (char *) (icmp6 + 1); - - icmp6->icmp6_type = ICMP6_ECHO_REQUEST; - icmp6->icmp6_code = 0; - /* The checksum will be calculated by the TCP/IP stack. */ - /* FIXME */ - icmp6->icmp6_cksum = 0; - icmp6->icmp6_id = htons (ph->ident); - icmp6->icmp6_seq = htons (ph->sequence); - - buflen = 4096 - sizeof (struct icmp6_hdr); - strncpy (data, ph->data, buflen); - datalen = strlen (data); - - buflen = datalen + sizeof (struct icmp6_hdr); - - dprintf ("Sending ICMPv6 package with ID 0x%04x\n", ph->ident); - - status = ping_sendto (obj, ph, buf, buflen); - if (status < 0) - { - perror ("ping_sendto"); - return (-1); - } - - dprintf ("sendto: status = %i\n", status); - - return (0); -} - -static int ping_send_all (pingobj_t *obj) -{ - pinghost_t *ph; - pinghost_t *ptr; - - int ret; - - ret = 0; - ph = obj->head; - - for (ptr = ph; ptr != NULL; ptr = ptr->next) - { - /* start timer.. The GNU `ping6' starts the timer before - * sending the packet, so I will do that too */ - if (gettimeofday (ptr->timer, NULL) == -1) - { - dprintf ("gettimeofday: %s\n", strerror (errno)); - timerclear (ptr->timer); - ret--; - continue; - } - else - { - dprintf ("timer set for hostname = %s\n", ptr->hostname); - } - - if (ptr->addrfamily == AF_INET6) - { - dprintf ("Sending ICMPv6 echo request to `%s'\n", ptr->hostname); - if (ping_send_one_ipv6 (obj, ptr) != 0) - { - timerclear (ptr->timer); - ret--; - continue; - } - } - else if (ptr->addrfamily == AF_INET) - { - dprintf ("Sending ICMPv4 echo request to `%s'\n", ptr->hostname); - if (ping_send_one_ipv4 (obj, ptr) != 0) - { - timerclear (ptr->timer); - ret--; - continue; - } - } - else /* this should not happen */ - { - dprintf ("Unknown address family: %i\n", ptr->addrfamily); - timerclear (ptr->timer); - ret--; - continue; - } - - ptr->sequence++; - } - - return (ret); -} - -/* - * Set the TTL of a socket protocol independently. - */ -static int ping_set_ttl (pinghost_t *ph, int ttl) -{ - int ret = -2; - - if (ph->addrfamily == AF_INET) - { - ret = setsockopt (ph->fd, IPPROTO_IP, IP_TTL, &ttl, sizeof (ttl)); - } - else if (ph->addrfamily == AF_INET6) - { - ret = setsockopt (ph->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof (ttl)); - } - - return (ret); -} - -static int ping_get_ident (void) -{ - int fd; - static int did_seed = 0; - - int retval; - - if (did_seed == 0) - { - if ((fd = open ("/dev/urandom", O_RDONLY)) != -1) - { - unsigned int seed; - - if (read (fd, &seed, sizeof (seed)) != -1) - { - did_seed = 1; - dprintf ("Random seed: %i\n", seed); - srandom (seed); - } - - close (fd); - } - else - { - dprintf ("open (/dev/urandom): %s\n", strerror (errno)); - } - } - - retval = (int) random (); - - dprintf ("Random number: %i\n", retval); - - return (retval); -} - -static pinghost_t *ping_alloc (void) -{ - pinghost_t *ph; - size_t ph_size; - - ph_size = sizeof (pinghost_t) - + sizeof (struct sockaddr_storage) - + sizeof (struct timeval); - - ph = (pinghost_t *) malloc (ph_size); - if (ph == NULL) - return (NULL); - - memset (ph, '\0', ph_size); - - ph->timer = (struct timeval *) (ph + 1); - ph->addr = (struct sockaddr_storage *) (ph->timer + 1); - - ph->addrlen = sizeof (struct sockaddr_storage); - ph->latency = -1.0; - ph->ident = ping_get_ident () & 0xFFFF; - - return (ph); -} - -static void ping_free (pinghost_t *ph) -{ - if (ph->hostname != NULL) - free (ph->hostname); - - if (ph->data != NULL) - free (ph->data); - - free (ph); -} - -/* - * public methods - */ -const char *ping_get_error (pingobj_t *obj) -{ - return (obj->errmsg); -} - -pingobj_t *ping_construct (void) -{ - pingobj_t *obj; - - if ((obj = (pingobj_t *) malloc (sizeof (pingobj_t))) == NULL) - return (NULL); - memset (obj, '\0', sizeof (pingobj_t)); - - obj->timeout = PING_DEF_TIMEOUT; - obj->ttl = PING_DEF_TTL; - obj->addrfamily = PING_DEF_AF; - obj->data = strdup (PING_DEF_DATA); - - return (obj); -} - -void ping_destroy (pingobj_t *obj) -{ - pinghost_t *current; - pinghost_t *next; - - current = obj->head; - next = NULL; - - while (current != NULL) - { - next = current->next; - ping_free (current); - current = next; - } - - if (obj->data != NULL) - free (obj->data); - - free (obj); - - return; -} - -int ping_setopt (pingobj_t *obj, int option, void *value) -{ - int ret = 0; - - switch (option) - { - case PING_OPT_TIMEOUT: - obj->timeout = *((double *) value); - if (obj->timeout < 0.0) - { - obj->timeout = PING_DEF_TIMEOUT; - ret = -1; - } - break; - - case PING_OPT_TTL: - obj->ttl = *((int *) value); - if ((obj->ttl < 1) || (obj->ttl > 255)) - { - obj->ttl = PING_DEF_TTL; - ret = -1; - } - break; - - case PING_OPT_AF: - obj->addrfamily = *((int *) value); - if ((obj->addrfamily != AF_UNSPEC) - && (obj->addrfamily != AF_INET) - && (obj->addrfamily != AF_INET6)) - { - obj->addrfamily = PING_DEF_AF; - ret = -1; - } - break; - - case PING_OPT_DATA: - if (obj->data != NULL) - { - free (obj->data); - obj->data = NULL; - } - obj->data = strdup ((const char *) value); - break; - - default: - ret = -2; - } /* switch (option) */ - - return (ret); -} /* int ping_setopt */ - - -int ping_send (pingobj_t *obj) -{ - int ret; - - if (ping_send_all (obj) < 0) - return (-1); - - if ((ret = ping_receive_all (obj)) < 0) - return (-2); - - return (ret); -} - -static pinghost_t *ping_host_search (pinghost_t *ph, const char *host) -{ - while (ph != NULL) - { - if (strcasecmp (ph->hostname, host) == 0) - break; - - ph = ph->next; - } - - return (ph); -} - -int ping_host_add (pingobj_t *obj, const char *host) -{ - pinghost_t *ph; - - struct addrinfo ai_hints; - struct addrinfo *ai_list, *ai_ptr; - int ai_return; - - dprintf ("host = %s\n", host); - - if (ping_host_search (obj->head, host) != NULL) - return (0); - - memset (&ai_hints, '\0', sizeof (ai_hints)); - ai_hints.ai_flags = 0; -#ifdef AI_ADDRCONFIG - ai_hints.ai_flags |= AI_ADDRCONFIG; -#endif - ai_hints.ai_family = obj->addrfamily; - ai_hints.ai_socktype = SOCK_RAW; - - if ((ph = ping_alloc ()) == NULL) - { - dprintf ("Out of memory!\n"); - return (-1); - } - - if ((ph->hostname = strdup (host)) == NULL) - { - dprintf ("Out of memory!\n"); - ping_set_error (obj, "strdup", strerror (errno)); - ping_free (ph); - return (-1); - } - - /* obj->data is not garuanteed to be != NULL */ - if ((ph->data = strdup (obj->data == NULL ? PING_DEF_DATA : obj->data)) == NULL) - { - dprintf ("Out of memory!\n"); - ping_set_error (obj, "strdup", strerror (errno)); - ping_free (ph); - return (-1); - } - - if ((ai_return = getaddrinfo (host, NULL, &ai_hints, &ai_list)) != 0) - { - dprintf ("getaddrinfo failed\n"); - ping_set_error (obj, "getaddrinfo", - (ai_return == EAI_SYSTEM) - ? strerror (errno) - : gai_strerror (ai_return)); - ping_free (ph); - return (-1); - } - - if (ai_list == NULL) - ping_set_error (obj, "getaddrinfo", "No hosts returned"); - - for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) - { - ph->fd = -1; - - if (ai_ptr->ai_family == AF_INET) - { - ai_ptr->ai_socktype = SOCK_RAW; - ai_ptr->ai_protocol = IPPROTO_ICMP; - } - else if (ai_ptr->ai_family == AF_INET6) - { - ai_ptr->ai_socktype = SOCK_RAW; - ai_ptr->ai_protocol = IPPROTO_ICMPV6; - } - else - { - char errmsg[PING_ERRMSG_LEN]; - - snprintf (errmsg, PING_ERRMSG_LEN, "Unknown `ai_family': %i", ai_ptr->ai_family); - errmsg[PING_ERRMSG_LEN - 1] = '\0'; - - dprintf (errmsg); - ping_set_error (obj, "getaddrinfo", errmsg); - continue; - } - - /* TODO: Move this to a static function `ping_open_socket' and - * call it whenever the socket dies. */ - ph->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); - if (ph->fd == -1) - { - dprintf ("socket: %s\n", strerror (errno)); - ping_set_error (obj, "socket", strerror (errno)); - continue; - } - -/* - * The majority vote of operating systems has decided that you don't need to - * bind here. This code should be reactivated to bind to a specific address, - * though. See the `-I' option of `ping(1)' (GNU). -octo - */ -#if 0 - if (bind (ph->fd, (struct sockaddr *) &sockaddr, sockaddr_len) == -1) - { - dprintf ("bind: %s\n", strerror (errno)); - ping_set_error (obj, "bind", strerror (errno)); - close (ph->fd); - ph->fd = -1; - continue; - } -#endif - - assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen); - memset (ph->addr, '\0', sizeof (struct sockaddr_storage)); - memcpy (ph->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen); - ph->addrlen = ai_ptr->ai_addrlen; - ph->addrfamily = ai_ptr->ai_family; - - break; - } - - freeaddrinfo (ai_list); - - if (ph->fd < 0) - { - free (ph->hostname); - free (ph); - return (-1); - } - - /* - * Adding in the front is much easier, but then the iterator will - * return the host that was added last as first host. That's just not - * nice. -octo - */ - if (obj->head == NULL) - { - obj->head = ph; - } - else - { - pinghost_t *hptr; - - hptr = obj->head; - while (hptr->next != NULL) - hptr = hptr->next; - - assert ((hptr != NULL) && (hptr->next == NULL)); - hptr->next = ph; - } - - ping_set_ttl (ph, obj->ttl); - - return (0); -} - -int ping_host_remove (pingobj_t *obj, const char *host) -{ - pinghost_t *pre, *cur; - - pre = NULL; - cur = obj->head; - - while (cur != NULL) - { - if (strcasecmp (host, cur->hostname)) - break; - - pre = cur; - cur = cur->next; - } - - if (cur == NULL) - { - ping_set_error (obj, "ping_host_remove", "Host not found"); - return (-1); - } - - if (pre == NULL) - obj->head = cur->next; - else - pre->next = cur->next; - - if (cur->fd >= 0) - close (cur->fd); - - ping_free (cur); - - return (0); -} - -pingobj_iter_t *ping_iterator_get (pingobj_t *obj) -{ - return ((pingobj_iter_t *) obj->head); -} - -pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter) -{ - return ((pingobj_iter_t *) iter->next); -} - -int ping_iterator_get_info (pingobj_iter_t *iter, int info, - void *buffer, size_t *buffer_len) -{ - int ret = EINVAL; - - size_t orig_buffer_len = *buffer_len; - - switch (info) - { - case PING_INFO_HOSTNAME: - ret = ENOMEM; - *buffer_len = strlen (iter->hostname); - if (orig_buffer_len <= *buffer_len) - break; - /* Since (orig_buffer_len > *buffer_len) `strncpy' - * will copy `*buffer_len' and pad the rest of - * `buffer' with null-bytes */ - strncpy (buffer, iter->hostname, orig_buffer_len); - ret = 0; - break; - - case PING_INFO_ADDRESS: - ret = getnameinfo ((struct sockaddr *) iter->addr, - iter->addrlen, - (char *) buffer, - *buffer_len, - NULL, 0, - NI_NUMERICHOST); - if (ret != 0) - { - if ((ret == EAI_MEMORY) -#ifdef EAI_OVERFLOW - || (ret == EAI_OVERFLOW) -#endif - ) - ret = ENOMEM; - else if (ret == EAI_SYSTEM) - /* XXX: Not thread-safe! */ - ret = errno; - else - ret = EINVAL; - } - break; - - case PING_INFO_FAMILY: - ret = ENOMEM; - *buffer_len = sizeof (int); - if (orig_buffer_len < sizeof (int)) - break; - *((int *) buffer) = iter->addrfamily; - ret = 0; - break; - - case PING_INFO_LATENCY: - ret = ENOMEM; - *buffer_len = sizeof (double); - if (orig_buffer_len < sizeof (double)) - break; - *((double *) buffer) = iter->latency; - ret = 0; - break; - - case PING_INFO_SEQUENCE: - ret = ENOMEM; - *buffer_len = sizeof (unsigned int); - if (orig_buffer_len < sizeof (unsigned int)) - break; - *((unsigned int *) buffer) = (unsigned int) iter->sequence; - ret = 0; - break; - - case PING_INFO_IDENT: - ret = ENOMEM; - *buffer_len = sizeof (uint16_t); - if (orig_buffer_len < sizeof (uint16_t)) - break; - *((uint16_t *) buffer) = (uint16_t) iter->ident; - ret = 0; - break; - - case PING_INFO_DATA: - ret = ENOMEM; - *buffer_len = strlen (iter->data); - if (orig_buffer_len < *buffer_len) - break; - strncpy ((char *) buffer, iter->data, orig_buffer_len); - ret = 0; - break; - } - - return (ret); -} - -void *ping_iterator_get_context (pingobj_iter_t *iter) -{ - return (iter->context); -} - -void ping_iterator_set_context (pingobj_iter_t *iter, void *context) -{ - iter->context = context; -} diff --git a/src/liboping/oping.h b/src/liboping/oping.h deleted file mode 100644 index 54d03101..00000000 --- a/src/liboping/oping.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Object oriented C module to send ICMP and ICMPv6 `echo's. - * Copyright (C) 2006 Florian octo Forster - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef OCTO_PING_H -#define OCTO_PING_H 1 - -#if HAVE_CONFIG_H -# include -#endif - -#if HAVE_STDLIB_H -# include -#endif -#if HAVE_UNISTD_H -# include -#endif -#if HAVE_SYS_SOCKET_H -# include -#endif - -/* - * Type definitions - */ -struct pinghost; -typedef struct pinghost pinghost_t; - -typedef pinghost_t pingobj_iter_t; - -struct pingobj; -typedef struct pingobj pingobj_t; - -#define PING_OPT_TIMEOUT 0x01 -#define PING_OPT_TTL 0x02 -#define PING_OPT_AF 0x04 -#define PING_OPT_DATA 0x08 - -#define PING_DEF_TIMEOUT 1.0 -#define PING_DEF_TTL 255 -#define PING_DEF_AF AF_UNSPEC -#define PING_DEF_DATA "Florian Forster http://verplant.org/" - -/* - * Method definitions - */ -pingobj_t *ping_construct (void); -void ping_destroy (pingobj_t *obj); - -int ping_setopt (pingobj_t *obj, int option, void *value); - -int ping_send (pingobj_t *obj); - -int ping_host_add (pingobj_t *obj, const char *host); -int ping_host_remove (pingobj_t *obj, const char *host); - -pingobj_iter_t *ping_iterator_get (pingobj_t *obj); -pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter); - -#define PING_INFO_HOSTNAME 1 -#define PING_INFO_ADDRESS 2 -#define PING_INFO_FAMILY 3 -#define PING_INFO_LATENCY 4 -#define PING_INFO_SEQUENCE 5 -#define PING_INFO_IDENT 6 -#define PING_INFO_DATA 7 -int ping_iterator_get_info (pingobj_iter_t *iter, int info, - void *buffer, size_t *buffer_len); - -const char *ping_get_error (pingobj_t *obj); - -void *ping_iterator_get_context (pingobj_iter_t *iter); -void ping_iterator_set_context (pingobj_iter_t *iter, void *context); - -#endif /* OCTO_PING_H */ diff --git a/src/logfile.c b/src/logfile.c index 382386b7..03af7a3f 100644 --- a/src/logfile.c +++ b/src/logfile.c @@ -143,7 +143,8 @@ static void logfile_print (const char *msg, time_t timestamp_time) return; } /* void logfile_print */ -static void logfile_log (int severity, const char *msg) +static void logfile_log (int severity, const char *msg, + user_data_t __attribute__((unused)) *user_data) { if (severity > log_level) return; @@ -151,7 +152,8 @@ static void logfile_log (int severity, const char *msg) logfile_print (msg, time (NULL)); } /* void logfile_log (int, const char *) */ -static int logfile_notification (const notification_t *n) +static int logfile_notification (const notification_t *n, + user_data_t __attribute__((unused)) *user_data) { char buf[1024] = ""; char *buf_ptr = buf; @@ -185,7 +187,8 @@ static int logfile_notification (const notification_t *n) buf[sizeof (buf) - 1] = '\0'; - logfile_print (buf, n->time); + logfile_print (buf, + (n->time > 0) ? n->time : time (NULL)); return (0); } /* int logfile_notification */ @@ -194,8 +197,9 @@ void module_register (void) { plugin_register_config ("logfile", logfile_config, config_keys, config_keys_num); - plugin_register_log ("logfile", logfile_log); - plugin_register_notification ("logfile", logfile_notification); + plugin_register_log ("logfile", logfile_log, /* user_data = */ NULL); + plugin_register_notification ("logfile", logfile_notification, + /* user_data = */ NULL); } /* void module_register (void) */ /* vim: set sw=4 ts=4 tw=78 noexpandtab : */ diff --git a/src/memcachec.c b/src/memcachec.c new file mode 100644 index 00000000..0868a9ce --- /dev/null +++ b/src/memcachec.c @@ -0,0 +1,533 @@ +/** + * collectd - src/memcachec.c + * Copyright (C) 2009 Doug MacEachern + * Copyright (C) 2006-2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Doug MacEachern + * Florian octo Forster + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" +#include "utils_match.h" + +#include + +/* + * Data types + */ +struct web_match_s; +typedef struct web_match_s web_match_t; +struct web_match_s /* {{{ */ +{ + char *regex; + int dstype; + char *type; + char *instance; + + cu_match_t *match; + + web_match_t *next; +}; /* }}} */ + +struct web_page_s; +typedef struct web_page_s web_page_t; +struct web_page_s /* {{{ */ +{ + char *instance; + + char *server; + char *key; + + memcached_st *memc; + char *buffer; + + web_match_t *matches; + + web_page_t *next; +}; /* }}} */ + +/* + * Global variables; + */ +static web_page_t *pages_g = NULL; + +/* + * Private functions + */ +static void cmc_web_match_free (web_match_t *wm) /* {{{ */ +{ + if (wm == NULL) + return; + + sfree (wm->regex); + sfree (wm->type); + sfree (wm->instance); + match_destroy (wm->match); + cmc_web_match_free (wm->next); + sfree (wm); +} /* }}} void cmc_web_match_free */ + +static void cmc_web_page_free (web_page_t *wp) /* {{{ */ +{ + if (wp == NULL) + return; + + if (wp->memc != NULL) + memcached_free(wp->memc); + wp->memc = NULL; + + sfree (wp->instance); + sfree (wp->server); + sfree (wp->key); + sfree (wp->buffer); + + cmc_web_match_free (wp->matches); + cmc_web_page_free (wp->next); + sfree (wp); +} /* }}} void cmc_web_page_free */ + +static int cmc_page_init_memc (web_page_t *wp) /* {{{ */ +{ + memcached_server_st *server; + + wp->memc = memcached_create(NULL); + if (wp->memc == NULL) + { + ERROR ("memcachec plugin: memcached_create failed."); + return (-1); + } + + server = memcached_servers_parse (wp->server); + memcached_server_push (wp->memc, server); + memcached_server_list_free (server); + + return (0); +} /* }}} int cmc_page_init_memc */ + +static int cmc_config_add_string (const char *name, char **dest, /* {{{ */ + oconfig_item_t *ci) +{ + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("memcachec plugin: `%s' needs exactly one string argument.", name); + return (-1); + } + + sfree (*dest); + *dest = strdup (ci->values[0].value.string); + if (*dest == NULL) + return (-1); + + return (0); +} /* }}} int cmc_config_add_string */ + +static int cmc_config_add_match_dstype (int *dstype_ret, /* {{{ */ + oconfig_item_t *ci) +{ + int dstype; + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("memcachec plugin: `DSType' needs exactly one string argument."); + return (-1); + } + + if (strncasecmp ("Gauge", ci->values[0].value.string, + strlen ("Gauge")) == 0) + { + dstype = UTILS_MATCH_DS_TYPE_GAUGE; + if (strcasecmp ("GaugeAverage", ci->values[0].value.string) == 0) + dstype |= UTILS_MATCH_CF_GAUGE_AVERAGE; + else if (strcasecmp ("GaugeMin", ci->values[0].value.string) == 0) + dstype |= UTILS_MATCH_CF_GAUGE_MIN; + else if (strcasecmp ("GaugeMax", ci->values[0].value.string) == 0) + dstype |= UTILS_MATCH_CF_GAUGE_MAX; + else if (strcasecmp ("GaugeLast", ci->values[0].value.string) == 0) + dstype |= UTILS_MATCH_CF_GAUGE_LAST; + else + dstype = 0; + } + else if (strncasecmp ("Counter", ci->values[0].value.string, + strlen ("Counter")) == 0) + { + dstype = UTILS_MATCH_DS_TYPE_COUNTER; + if (strcasecmp ("CounterSet", ci->values[0].value.string) == 0) + dstype |= UTILS_MATCH_CF_COUNTER_SET; + else if (strcasecmp ("CounterAdd", ci->values[0].value.string) == 0) + dstype |= UTILS_MATCH_CF_COUNTER_ADD; + else if (strcasecmp ("CounterInc", ci->values[0].value.string) == 0) + dstype |= UTILS_MATCH_CF_COUNTER_INC; + else + dstype = 0; + } + else + { + dstype = 0; + } + + if (dstype == 0) + { + WARNING ("memcachec plugin: `%s' is not a valid argument to `DSType'.", + ci->values[0].value.string); + return (-1); + } + + *dstype_ret = dstype; + return (0); +} /* }}} int cmc_config_add_match_dstype */ + +static int cmc_config_add_match (web_page_t *page, /* {{{ */ + oconfig_item_t *ci) +{ + web_match_t *match; + int status; + int i; + + if (ci->values_num != 0) + { + WARNING ("memcachec plugin: Ignoring arguments for the `Match' block."); + } + + match = (web_match_t *) malloc (sizeof (*match)); + if (match == NULL) + { + ERROR ("memcachec plugin: malloc failed."); + return (-1); + } + memset (match, 0, sizeof (*match)); + + status = 0; + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Regex", child->key) == 0) + status = cmc_config_add_string ("Regex", &match->regex, child); + else if (strcasecmp ("DSType", child->key) == 0) + status = cmc_config_add_match_dstype (&match->dstype, child); + else if (strcasecmp ("Type", child->key) == 0) + status = cmc_config_add_string ("Type", &match->type, child); + else if (strcasecmp ("Instance", child->key) == 0) + status = cmc_config_add_string ("Instance", &match->instance, child); + else + { + WARNING ("memcachec plugin: Option `%s' not allowed here.", child->key); + status = -1; + } + + if (status != 0) + break; + } /* for (i = 0; i < ci->children_num; i++) */ + + while (status == 0) + { + if (match->regex == NULL) + { + WARNING ("memcachec plugin: `Regex' missing in `Match' block."); + status = -1; + } + + if (match->type == NULL) + { + WARNING ("memcachec plugin: `Type' missing in `Match' block."); + status = -1; + } + + if (match->dstype == 0) + { + WARNING ("memcachec plugin: `DSType' missing in `Match' block."); + status = -1; + } + + break; + } /* while (status == 0) */ + + if (status != 0) + return (status); + + match->match = match_create_simple (match->regex, match->dstype); + if (match->match == NULL) + { + ERROR ("memcachec plugin: tail_match_add_match_simple failed."); + cmc_web_match_free (match); + return (-1); + } + else + { + web_match_t *prev; + + prev = page->matches; + while ((prev != NULL) && (prev->next != NULL)) + prev = prev->next; + + if (prev == NULL) + page->matches = match; + else + prev->next = match; + } + + return (0); +} /* }}} int cmc_config_add_match */ + +static int cmc_config_add_page (oconfig_item_t *ci) /* {{{ */ +{ + web_page_t *page; + int status; + int i; + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("memcachec plugin: `Page' blocks need exactly one string argument."); + return (-1); + } + + page = (web_page_t *) malloc (sizeof (*page)); + if (page == NULL) + { + ERROR ("memcachec plugin: malloc failed."); + return (-1); + } + memset (page, 0, sizeof (*page)); + page->server = NULL; + page->key = NULL; + + page->instance = strdup (ci->values[0].value.string); + if (page->instance == NULL) + { + ERROR ("memcachec plugin: strdup failed."); + sfree (page); + return (-1); + } + + /* Process all children */ + status = 0; + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Server", child->key) == 0) + status = cmc_config_add_string ("Server", &page->server, child); + if (strcasecmp ("Key", child->key) == 0) + status = cmc_config_add_string ("Key", &page->key, child); + else if (strcasecmp ("Match", child->key) == 0) + /* Be liberal with failing matches => don't set `status'. */ + cmc_config_add_match (page, child); + else + { + WARNING ("memcachec plugin: Option `%s' not allowed here.", child->key); + status = -1; + } + + if (status != 0) + break; + } /* for (i = 0; i < ci->children_num; i++) */ + + /* Additionial sanity checks and libCURL initialization. */ + while (status == 0) + { + if (page->server == NULL) + { + WARNING ("memcachec plugin: `Server' missing in `Page' block."); + status = -1; + } + + if (page->key == NULL) + { + WARNING ("memcachec plugin: `Key' missing in `Page' block."); + status = -1; + } + + if (page->matches == NULL) + { + assert (page->instance != NULL); + WARNING ("memcachec plugin: No (valid) `Match' block " + "within `Page' block `%s'.", page->instance); + status = -1; + } + + if (status == 0) + status = cmc_page_init_memc (page); + + break; + } /* while (status == 0) */ + + if (status != 0) + { + cmc_web_page_free (page); + return (status); + } + + /* Add the new page to the linked list */ + if (pages_g == NULL) + pages_g = page; + else + { + web_page_t *prev; + + prev = pages_g; + while ((prev != NULL) && (prev->next != NULL)) + prev = prev->next; + prev->next = page; + } + + return (0); +} /* }}} int cmc_config_add_page */ + +static int cmc_config (oconfig_item_t *ci) /* {{{ */ +{ + int success; + int errors; + int status; + int i; + + success = 0; + errors = 0; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Page", child->key) == 0) + { + status = cmc_config_add_page (child); + if (status == 0) + success++; + else + errors++; + } + else + { + WARNING ("memcachec plugin: Option `%s' not allowed here.", child->key); + errors++; + } + } + + if ((success == 0) && (errors > 0)) + { + ERROR ("memcachec plugin: All statements failed."); + return (-1); + } + + return (0); +} /* }}} int cmc_config */ + +static int cmc_init (void) /* {{{ */ +{ + if (pages_g == NULL) + { + INFO ("memcachec plugin: No pages have been defined."); + return (-1); + } + return (0); +} /* }}} int cmc_init */ + +static void cmc_submit (const web_page_t *wp, const web_match_t *wm, /* {{{ */ + const cu_match_value_t *mv) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + values[0] = mv->value; + + vl.values = values; + vl.values_len = 1; + vl.time = time (NULL); + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "memcachec", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, wp->instance, sizeof (vl.plugin_instance)); + sstrncpy (vl.type, wm->type, sizeof (vl.type)); + sstrncpy (vl.type_instance, wm->instance, sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} /* }}} void cmc_submit */ + +static int cmc_read_page (web_page_t *wp) /* {{{ */ +{ + web_match_t *wm; + memcached_return rc; + size_t string_length; + uint32_t flags; + int status; + + if (wp->memc == NULL) + return (-1); + + wp->buffer = memcached_get (wp->memc, wp->key, strlen (wp->key), + &string_length, &flags, &rc); + if (rc != MEMCACHED_SUCCESS) + { + ERROR ("memcachec plugin: memcached_get failed: %s", + memcached_strerror (wp->memc, rc)); + return (-1); + } + + for (wm = wp->matches; wm != NULL; wm = wm->next) + { + cu_match_value_t *mv; + + status = match_apply (wm->match, wp->buffer); + if (status != 0) + { + WARNING ("memcachec plugin: match_apply failed."); + continue; + } + + mv = match_get_user_data (wm->match); + if (mv == NULL) + { + WARNING ("memcachec plugin: match_get_user_data returned NULL."); + continue; + } + + cmc_submit (wp, wm, mv); + } /* for (wm = wp->matches; wm != NULL; wm = wm->next) */ + + sfree (wp->buffer); + + return (0); +} /* }}} int cmc_read_page */ + +static int cmc_read (void) /* {{{ */ +{ + web_page_t *wp; + + for (wp = pages_g; wp != NULL; wp = wp->next) + cmc_read_page (wp); + + return (0); +} /* }}} int cmc_read */ + +static int cmc_shutdown (void) /* {{{ */ +{ + cmc_web_page_free (pages_g); + pages_g = NULL; + + return (0); +} /* }}} int cmc_shutdown */ + +void module_register (void) +{ + plugin_register_complex_config ("memcachec", cmc_config); + plugin_register_init ("memcachec", cmc_init); + plugin_register_read ("memcachec", cmc_read); + plugin_register_shutdown ("memcachec", cmc_shutdown); +} /* void module_register */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/mysql.c b/src/mysql.c index 1c009a07..a129f40f 100644 --- a/src/mysql.c +++ b/src/mysql.c @@ -1,6 +1,8 @@ /** * collectd - src/mysql.c - * Copyright (C) 2006,2007 Florian octo Forster + * Copyright (C) 2006-2009 Florian octo Forster + * Copyright (C) 2009 Doug MacEachern + * Copyright (C) 2009 Sebastian tokkee Harl * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -17,6 +19,9 @@ * * Authors: * Florian octo Forster + * Mirko Buffoni + * Doug MacEachern + * Sebastian tokkee Harl **/ #include "collectd.h" @@ -32,146 +37,439 @@ /* TODO: Understand `Select_*' and possibly do that stuff as well.. */ -static const char *config_keys[] = +struct mysql_database_s /* {{{ */ { - "Host", - "User", - "Password", - "Database", - "Port", - "Socket", - NULL + /* instance == NULL => legacy mode */ + char *instance; + char *host; + char *user; + char *pass; + char *database; + char *socket; + int port; + + int master_stats; + int slave_stats; + + int slave_notif; + int slave_io_running; + int slave_sql_running; + + MYSQL *con; + int state; }; -static int config_keys_num = 6; +typedef struct mysql_database_s mysql_database_t; /* }}} */ -static char *host = "localhost"; -static char *user; -static char *pass; -static char *db = NULL; -static char *socket = NULL; -static int port = 0; +static int mysql_read (user_data_t *ud); -static MYSQL *getconnection (void) +static void mysql_database_free (void *arg) /* {{{ */ { - static MYSQL *con; - static int state; + mysql_database_t *db; - static int wait_for = 0; - static int wait_increase = 60; + DEBUG ("mysql plugin: mysql_database_free (arg = %p);", arg); - if (state != 0) + db = (mysql_database_t *) arg; + + if (db == NULL) + return; + + if (db->con != NULL) + mysql_close (db->con); + + sfree (db->host); + sfree (db->user); + sfree (db->pass); + sfree (db->socket); + sfree (db->instance); + sfree (db->database); + sfree (db); +} /* }}} void mysql_database_free */ + +/* Configuration handling functions {{{ + * + * + * + * Host "localhost" + * Port 22000 + * ... + * + * + */ + +static int mysql_config_set_string (char **ret_string, /* {{{ */ + oconfig_item_t *ci) +{ + char *string; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) { - int err; - if ((err = mysql_ping (con)) != 0) + WARNING ("mysql plugin: The `%s' config option " + "needs exactly one string argument.", ci->key); + return (-1); + } + + string = strdup (ci->values[0].value.string); + if (string == NULL) + { + ERROR ("mysql plugin: strdup failed."); + return (-1); + } + + if (*ret_string != NULL) + free (*ret_string); + *ret_string = string; + + return (0); +} /* }}} int mysql_config_set_string */ + +static int mysql_config_set_int (int *ret_int, /* {{{ */ + oconfig_item_t *ci) +{ + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) + { + WARNING ("mysql plugin: The `%s' config option " + "needs exactly one string argument.", ci->key); + return (-1); + } + + *ret_int = ci->values[0].value.number; + + return (0); +} /* }}} int mysql_config_set_int */ + +static int mysql_config_set_boolean (int *ret_boolean, /* {{{ */ + oconfig_item_t *ci) +{ + int status = 0; + + if (ci->values_num != 1) + status = -1; + + if (status == 0) + { + if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN) + *ret_boolean = ci->values[0].value.boolean; + else if (ci->values[0].type == OCONFIG_TYPE_STRING) { - WARNING ("mysql_ping failed: %s", mysql_error (con)); - state = 0; + if (IS_TRUE (ci->values[0].value.string)) + *ret_boolean = 1; + else if (IS_FALSE (ci->values[0].value.string)) + *ret_boolean = 0; + else + status = -1; } else + status = -1; + } + + if (status != 0) + { + WARNING ("mysql plugin: The `%s' config option " + "needs exactly one boolean argument.", ci->key); + return (-1); + } + return (0); +} /* }}} mysql_config_set_boolean */ + +static int mysql_config (oconfig_item_t *ci) /* {{{ */ +{ + mysql_database_t *db; + int plugin_block; + int status = 0; + int i; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("mysql plugin: The `Database' block " + "needs exactly one string argument."); + return (-1); + } + + db = (mysql_database_t *) malloc (sizeof (*db)); + if (db == NULL) + { + ERROR ("mysql plugin: malloc failed."); + return (-1); + } + memset (db, 0, sizeof (*db)); + + /* initialize all the pointers */ + db->host = NULL; + db->user = NULL; + db->pass = NULL; + db->database = NULL; + db->socket = NULL; + db->con = NULL; + + /* trigger a notification, if it's not running */ + db->slave_io_running = 1; + db->slave_sql_running = 1; + + plugin_block = 1; + if (strcasecmp ("Plugin", ci->key) == 0) + { + db->instance = NULL; + } + else if (strcasecmp ("Database", ci->key) == 0) + { + plugin_block = 0; + status = mysql_config_set_string (&db->instance, ci); + if (status != 0) { - state = 1; - return (con); + sfree (db); + return (status); } + assert (db->instance != NULL); + } + else + { + ERROR ("mysql plugin: mysql_config: " + "Invalid key: %s", ci->key); + return (-1); } - if (wait_for > 0) + /* Fill the `mysql_database_t' structure.. */ + for (i = 0; i < ci->children_num; i++) { - wait_for -= interval_g; - return (NULL); + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Host", child->key) == 0) + status = mysql_config_set_string (&db->host, child); + else if (strcasecmp ("User", child->key) == 0) + status = mysql_config_set_string (&db->user, child); + else if (strcasecmp ("Password", child->key) == 0) + status = mysql_config_set_string (&db->pass, child); + else if (strcasecmp ("Port", child->key) == 0) + status = mysql_config_set_int (&db->port, child); + else if (strcasecmp ("Socket", child->key) == 0) + status = mysql_config_set_string (&db->socket, child); + /* Check if we're currently handling the `Plugin' block. If so, + * handle `Database' _blocks_, too. */ + else if ((plugin_block != 0) + && (strcasecmp ("Database", child->key) == 0) + && (child->children != NULL)) + { + /* If `plugin_block > 1', there has been at least one + * `Database' block */ + plugin_block++; + status = mysql_config (child); + } + /* Now handle ordinary `Database' options (without children) */ + else if ((strcasecmp ("Database", child->key) == 0) + && (child->children == NULL)) + status = mysql_config_set_string (&db->database, child); + else if (strcasecmp ("MasterStats", child->key) == 0) + status = mysql_config_set_boolean (&db->master_stats, child); + else if (strcasecmp ("SlaveStats", child->key) == 0) + status = mysql_config_set_boolean (&db->slave_stats, child); + else if (strcasecmp ("SlaveNotifications", child->key) == 0) + status = mysql_config_set_boolean (&db->slave_notif, child); + else + { + WARNING ("mysql plugin: Option `%s' not allowed here.", child->key); + status = -1; + } + + if (status != 0) + break; } - wait_for = wait_increase; - wait_increase *= 2; - if (wait_increase > 86400) - wait_increase = 86400; + /* Check if there were any `Database' blocks. */ + if (plugin_block > 1) + { + /* There were connection blocks. Don't use any legacy stuff. */ + if ((db->host != NULL) + || (db->user != NULL) + || (db->pass != NULL) + || (db->database != NULL) + || (db->socket != NULL) + || (db->port != 0)) + { + WARNING ("mysql plugin: At least one " + "block has been found. The legacy " + "configuration will be ignored."); + } + mysql_database_free (db); + return (0); + } + else if (plugin_block != 0) + { + WARNING ("mysql plugin: You're using the legacy " + "configuration options. Please consider " + "updating your configuration!"); + } + + /* Check that all necessary options have been given. */ + while (status == 0) + { + /* Zero is allowed and automatically handled by + * `mysql_real_connect'. */ + if ((db->port < 0) || (db->port > 65535)) + { + ERROR ("mysql plugin: Database %s: Port number out " + "of range: %i", + (db->instance != NULL) + ? db->instance + : "", + db->port); + status = -1; + } + break; + } /* while (status == 0) */ + + /* If all went well, register this database for reading */ + if (status == 0) + { + user_data_t ud; + char cb_name[DATA_MAX_NAME_LEN]; - if ((con = mysql_init (con)) == NULL) + DEBUG ("mysql plugin: Registering new read callback: %s", + (db->database != NULL) ? db->database : ""); + + memset (&ud, 0, sizeof (ud)); + ud.data = (void *) db; + ud.free_func = mysql_database_free; + + if (db->database != NULL) + ssnprintf (cb_name, sizeof (cb_name), "mysql-%s", + db->database); + else + sstrncpy (cb_name, "mysql", sizeof (cb_name)); + + plugin_register_complex_read (cb_name, mysql_read, + /* interval = */ NULL, &ud); + } + else + { + mysql_database_free (db); + return (-1); + } + + return (0); +} /* }}} int mysql_config */ + +/* }}} End of configuration handling functions */ + +static MYSQL *getconnection (mysql_database_t *db) +{ + if (db->state != 0) { - ERROR ("mysql_init failed: %s", mysql_error (con)); - state = 0; + int err; + if ((err = mysql_ping (db->con)) != 0) + { + WARNING ("mysql_ping failed: %s", mysql_error (db->con)); + db->state = 0; + } + else + { + db->state = 1; + return (db->con); + } + } + + if ((db->con = mysql_init (db->con)) == NULL) + { + ERROR ("mysql_init failed: %s", mysql_error (db->con)); + db->state = 0; return (NULL); } - if (mysql_real_connect (con, host, user, pass, db, port, socket, 0) == NULL) + if (mysql_real_connect (db->con, db->host, db->user, db->pass, + db->database, db->port, db->socket, 0) == NULL) { - ERROR ("mysql_real_connect failed: %s", mysql_error (con)); - state = 0; + ERROR ("mysql plugin: Failed to connect to database %s " + "at server %s: %s", + (db->database != NULL) ? db->database : "", + (db->host != NULL) ? db->host : "localhost", + mysql_error (db->con)); + db->state = 0; return (NULL); } else { - state = 1; - wait_for = 0; - wait_increase = 60; - return (con); + INFO ("mysql plugin: Sucessfully connected to database %s " + "at server %s (server version: %s, protocol version: %d)", + (db->database != NULL) ? db->database : "", + mysql_get_host_info (db->con), + mysql_get_server_info (db->con), + mysql_get_proto_info (db->con)); + db->state = 1; + return (db->con); } -} /* static MYSQL *getconnection (void) */ +} /* static MYSQL *getconnection (mysql_database_t *db) */ -static int config (const char *key, const char *value) +static void set_host (mysql_database_t *db, char *buf, size_t buflen) { - if (strcasecmp (key, "host") == 0) - return ((host = strdup (value)) == NULL ? 1 : 0); - else if (strcasecmp (key, "user") == 0) - return ((user = strdup (value)) == NULL ? 1 : 0); - else if (strcasecmp (key, "password") == 0) - return ((pass = strdup (value)) == NULL ? 1 : 0); - else if (strcasecmp (key, "database") == 0) - return ((db = strdup (value)) == NULL ? 1 : 0); - else if (strcasecmp (key, "socket") == 0) - return ((socket = strdup (value)) == NULL ? 1 : 0); - else if (strcasecmp (key, "port") == 0) + /* XXX legacy mode - use hostname_g */ + if (db->instance == NULL) + sstrncpy (buf, hostname_g, buflen); + else { - char *endptr = NULL; - int temp; - - errno = 0; - temp = strtol (value, &endptr, 0); - if ((errno != 0) || (value == endptr)) - { - ERROR ("mysql plugin: Invalid \"Port\" argument: %s", - value); - port = 0; - return (1); - } - else if ((temp < 0) || (temp >= 65535)) - { - ERROR ("mysql plugin: Port number out of range: %i", - temp); - port = 0; - return (1); - } - - port = temp; - return (0); + if ((db->host == NULL) + || (strcmp ("", db->host) == 0) + || (strcmp ("localhost", db->host) == 0)) + sstrncpy (buf, hostname_g, buflen); + else + sstrncpy (buf, db->host, buflen); } +} + +static void set_plugin_instance (mysql_database_t *db, + char *buf, size_t buflen) +{ + /* XXX legacy mode - no plugin_instance */ + if (db->instance == NULL) + sstrncpy (buf, "", buflen); else - return (-1); -} /* int config */ + sstrncpy (buf, db->instance, buflen); +} -static void counter_submit (const char *type, const char *type_instance, - counter_t value) +static void submit (const char *type, const char *type_instance, + value_t *values, size_t values_len, mysql_database_t *db) { - value_t values[1]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = value; + vl.values = values; + vl.values_len = values_len; + + set_host (db, vl.host, sizeof (vl.host)); - vl.values = values; - vl.values_len = 1; - sstrncpy (vl.host, hostname_g, sizeof (vl.host)); sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin)); + set_plugin_instance (db, vl.plugin_instance, sizeof (vl.plugin_instance)); + sstrncpy (vl.type, type, sizeof (vl.type)); - sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); + if (type_instance != NULL) + sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); plugin_dispatch_values (&vl); +} /* submit */ + +static void counter_submit (const char *type, const char *type_instance, + counter_t value, mysql_database_t *db) +{ + value_t values[1]; + + values[0].counter = value; + submit (type, type_instance, values, STATIC_ARRAY_SIZE (values), db); } /* void counter_submit */ +static void gauge_submit (const char *type, const char *type_instance, + gauge_t value, mysql_database_t *db) +{ + value_t values[1]; + + values[0].gauge = value; + submit (type, type_instance, values, STATIC_ARRAY_SIZE (values), db); +} /* void gauge_submit */ + static void qcache_submit (counter_t hits, counter_t inserts, counter_t not_cached, counter_t lowmem_prunes, - gauge_t queries_in_cache) + gauge_t queries_in_cache, mysql_database_t *db) { value_t values[5]; - value_list_t vl = VALUE_LIST_INIT; values[0].counter = hits; values[1].counter = inserts; @@ -179,59 +477,228 @@ static void qcache_submit (counter_t hits, counter_t inserts, values[3].counter = lowmem_prunes; values[4].gauge = queries_in_cache; - vl.values = values; - vl.values_len = 5; - sstrncpy (vl.host, hostname_g, sizeof (vl.host)); - sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin)); - sstrncpy (vl.type, "mysql_qcache", sizeof (vl.type)); - - plugin_dispatch_values (&vl); + submit ("mysql_qcache", NULL, values, STATIC_ARRAY_SIZE (values), db); } /* void qcache_submit */ static void threads_submit (gauge_t running, gauge_t connected, gauge_t cached, - counter_t created) + counter_t created, mysql_database_t *db) { value_t values[4]; - value_list_t vl = VALUE_LIST_INIT; values[0].gauge = running; values[1].gauge = connected; values[2].gauge = cached; values[3].counter = created; - vl.values = values; - vl.values_len = 4; - sstrncpy (vl.host, hostname_g, sizeof (vl.host)); - sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin)); - sstrncpy (vl.type, "mysql_threads", sizeof (vl.type)); - - plugin_dispatch_values (&vl); + submit ("mysql_threads", NULL, values, STATIC_ARRAY_SIZE (values), db); } /* void threads_submit */ -static void traffic_submit (counter_t rx, counter_t tx) +static void traffic_submit (counter_t rx, counter_t tx, mysql_database_t *db) { value_t values[2]; - value_list_t vl = VALUE_LIST_INIT; values[0].counter = rx; values[1].counter = tx; - vl.values = values; - vl.values_len = 2; - sstrncpy (vl.host, hostname_g, sizeof (vl.host)); - sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin)); - sstrncpy (vl.type, "mysql_octets", sizeof (vl.type)); - - plugin_dispatch_values (&vl); + submit ("mysql_octets", NULL, values, STATIC_ARRAY_SIZE (values), db); } /* void traffic_submit */ -static int mysql_read (void) +static MYSQL_RES *exec_query (MYSQL *con, const char *query) +{ + MYSQL_RES *res; + + int query_len = strlen (query); + + if (mysql_real_query (con, query, query_len)) + { + ERROR ("mysql plugin: Failed to execute query: %s", + mysql_error (con)); + INFO ("mysql plugin: SQL query was: %s", query); + return (NULL); + } + + res = mysql_store_result (con); + if (res == NULL) + { + ERROR ("mysql plugin: Failed to store query result: %s", + mysql_error (con)); + INFO ("mysql plugin: SQL query was: %s", query); + return (NULL); + } + + return (res); +} /* exec_query */ + +static int mysql_read_master_stats (mysql_database_t *db, MYSQL *con) { + MYSQL_RES *res; + MYSQL_ROW row; + + char *query; + int field_num; + unsigned long long position; + + query = "SHOW MASTER STATUS"; + + res = exec_query (con, query); + if (res == NULL) + return (-1); + + row = mysql_fetch_row (res); + if (row == NULL) + { + ERROR ("mysql plugin: Failed to get master statistics: " + "`%s' did not return any rows.", query); + return (-1); + } + + field_num = mysql_num_fields (res); + if (field_num < 2) + { + ERROR ("mysql plugin: Failed to get master statistics: " + "`%s' returned less than two columns.", query); + return (-1); + } + + position = atoll (row[1]); + counter_submit ("mysql_log_position", "master-bin", position, db); + + row = mysql_fetch_row (res); + if (row != NULL) + WARNING ("mysql plugin: `%s' returned more than one row - " + "ignoring further results.", query); + + mysql_free_result (res); + + return (0); +} /* mysql_read_master_stats */ + +static int mysql_read_slave_stats (mysql_database_t *db, MYSQL *con) +{ + MYSQL_RES *res; + MYSQL_ROW row; + + char *query; + int field_num; + + /* WTF? libmysqlclient does not seem to provide any means to + * translate a column name to a column index ... :-/ */ + const int READ_MASTER_LOG_POS_IDX = 6; + const int SLAVE_IO_RUNNING_IDX = 10; + const int SLAVE_SQL_RUNNING_IDX = 11; + const int EXEC_MASTER_LOG_POS_IDX = 21; + const int SECONDS_BEHIND_MASTER_IDX = 32; + + query = "SHOW SLAVE STATUS"; + + res = exec_query (con, query); + if (res == NULL) + return (-1); + + row = mysql_fetch_row (res); + if (row == NULL) + { + ERROR ("mysql plugin: Failed to get slave statistics: " + "`%s' did not return any rows.", query); + return (-1); + } + + field_num = mysql_num_fields (res); + if (field_num < 33) + { + ERROR ("mysql plugin: Failed to get slave statistics: " + "`%s' returned less than 33 columns.", query); + return (-1); + } + + if (db->slave_stats) + { + unsigned long long counter; + double gauge; + + counter = atoll (row[READ_MASTER_LOG_POS_IDX]); + counter_submit ("mysql_log_position", "slave-read", counter, db); + + counter = atoll (row[EXEC_MASTER_LOG_POS_IDX]); + counter_submit ("mysql_log_position", "slave-exec", counter, db); + + if (row[SECONDS_BEHIND_MASTER_IDX] != NULL) + { + gauge = atof (row[SECONDS_BEHIND_MASTER_IDX]); + gauge_submit ("time_offset", NULL, gauge, db); + } + } + + if (db->slave_notif) + { + notification_t n = { 0, time (NULL), "", "", + "mysql", "", "time_offset", "", NULL }; + + char *io, *sql; + + io = row[SLAVE_IO_RUNNING_IDX]; + sql = row[SLAVE_SQL_RUNNING_IDX]; + + set_host (db, n.host, sizeof (n.host)); + set_plugin_instance (db, + n.plugin_instance, sizeof (n.plugin_instance)); + + if (((io == NULL) || (strcasecmp (io, "yes") != 0)) + && (db->slave_io_running)) + { + n.severity = NOTIF_WARNING; + ssnprintf (n.message, sizeof (n.message), + "slave I/O thread not started or not connected to master"); + plugin_dispatch_notification (&n); + db->slave_io_running = 0; + } + else if (((io != NULL) && (strcasecmp (io, "yes") == 0)) + && (! db->slave_io_running)) + { + n.severity = NOTIF_OKAY; + ssnprintf (n.message, sizeof (n.message), + "slave I/O thread started and connected to master"); + plugin_dispatch_notification (&n); + db->slave_io_running = 1; + } + + if (((sql == NULL) || (strcasecmp (sql, "yes") != 0)) + && (db->slave_sql_running)) + { + n.severity = NOTIF_WARNING; + ssnprintf (n.message, sizeof (n.message), + "slave SQL thread not started"); + plugin_dispatch_notification (&n); + db->slave_sql_running = 0; + } + else if (((sql != NULL) && (strcasecmp (sql, "yes") == 0)) + && (! db->slave_sql_running)) + { + n.severity = NOTIF_OKAY; + ssnprintf (n.message, sizeof (n.message), + "slave SQL thread started"); + plugin_dispatch_notification (&n); + db->slave_sql_running = 0; + } + } + + row = mysql_fetch_row (res); + if (row != NULL) + WARNING ("mysql plugin: `%s' returned more than one row - " + "ignoring further results.", query); + + mysql_free_result (res); + + return (0); +} /* mysql_read_slave_stats */ + +static int mysql_read (user_data_t *ud) +{ + mysql_database_t *db; MYSQL *con; MYSQL_RES *res; MYSQL_ROW row; char *query; - int query_len; int field_num; unsigned long long qcache_hits = 0ULL; @@ -248,29 +715,25 @@ static int mysql_read (void) unsigned long long traffic_incoming = 0ULL; unsigned long long traffic_outgoing = 0ULL; + if ((ud == NULL) || (ud->data == NULL)) + { + ERROR ("mysql plugin: mysql_database_read: Invalid user data."); + return (-1); + } + + db = (mysql_database_t *) ud->data; + /* An error message will have been printed in this case */ - if ((con = getconnection ()) == NULL) + if ((con = getconnection (db)) == NULL) return (-1); query = "SHOW STATUS"; if (mysql_get_server_version (con) >= 50002) query = "SHOW GLOBAL STATUS"; - query_len = strlen (query); - - if (mysql_real_query (con, query, query_len)) - { - ERROR ("mysql_real_query failed: %s\n", - mysql_error (con)); - return (-1); - } - - if ((res = mysql_store_result (con)) == NULL) - { - ERROR ("mysql_store_result failed: %s\n", - mysql_error (con)); + res = exec_query (con, query); + if (res == NULL) return (-1); - } field_num = mysql_num_fields (res); while ((row = mysql_fetch_row (res))) @@ -288,14 +751,14 @@ static int mysql_read (void) /* Ignore `prepared statements' */ if (strncmp (key, "Com_stmt_", 9) != 0) - counter_submit ("mysql_commands", key + 4, val); + counter_submit ("mysql_commands", key + 4, val, db); } else if (strncmp (key, "Handler_", 8) == 0) { if (val == 0ULL) continue; - counter_submit ("mysql_handler", key + 8, val); + counter_submit ("mysql_handler", key + 8, val, db); } else if (strncmp (key, "Qcache_", 7) == 0) { @@ -336,21 +799,24 @@ static int mysql_read (void) || (qcache_not_cached != 0ULL) || (qcache_lowmem_prunes != 0ULL)) qcache_submit (qcache_hits, qcache_inserts, qcache_not_cached, - qcache_lowmem_prunes, qcache_queries_in_cache); + qcache_lowmem_prunes, qcache_queries_in_cache, db); if (threads_created != 0ULL) threads_submit (threads_running, threads_connected, - threads_cached, threads_created); + threads_cached, threads_created, db); + + traffic_submit (traffic_incoming, traffic_outgoing, db); - traffic_submit (traffic_incoming, traffic_outgoing); + if (db->master_stats) + mysql_read_master_stats (db, con); - /* mysql_close (con); */ + if ((db->slave_stats) || (db->slave_notif)) + mysql_read_slave_stats (db, con); return (0); } /* int mysql_read */ void module_register (void) { - plugin_register_config ("mysql", config, config_keys, config_keys_num); - plugin_register_read ("mysql", mysql_read); + plugin_register_complex_config ("mysql", mysql_config); } /* void module_register */ diff --git a/src/network.c b/src/network.c index 902f270e..51b9922d 100644 --- a/src/network.c +++ b/src/network.c @@ -1,6 +1,6 @@ /** * collectd - src/network.c - * Copyright (C) 2005-2008 Florian octo Forster + * Copyright (C) 2005-2009 Florian octo Forster * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -46,6 +46,10 @@ # include #endif +#if HAVE_GCRYPT_H +# include +#endif + /* 1500 - 40 - 8 = Ethernet packet - IPv6 header - UDP header */ /* #define BUFF_SIZE 1452 */ @@ -57,8 +61,19 @@ # endif #endif /* !IP_ADD_MEMBERSHIP */ +/* Buffer size to allocate. */ #define BUFF_SIZE 1024 +/* + * Maximum size required for encryption / signing: + * Type/length: 4 + * Hash/orig length: 32 + * Padding (up to): 16 + * -------------------- + * 52 + */ +#define BUFF_SIG_SIZE 52 + /* * Private data types */ @@ -67,6 +82,16 @@ typedef struct sockent int fd; struct sockaddr_storage *addr; socklen_t addrlen; + +#define SECURITY_LEVEL_NONE 0 +#if HAVE_GCRYPT_H +# define SECURITY_LEVEL_SIGN 1 +# define SECURITY_LEVEL_ENCRYPT 2 + int security_level; + char *shared_secret; + gcry_cipher_hd_t cypher; +#endif /* HAVE_GCRYPT_H */ + struct sockent *next; } sockent_t; @@ -136,10 +161,27 @@ struct part_values_s }; typedef struct part_values_s part_values_t; +struct part_signature_sha256_s +{ + part_header_t head; + char hash[32]; +}; +typedef struct part_signature_sha256_s part_signature_sha256_t; + +struct part_encryption_aes256_s +{ + part_header_t head; + uint16_t orig_length; + uint16_t random; + char hash[28]; +}; +typedef struct part_encryption_aes256_s part_encryption_aes256_t; + struct receive_list_entry_s { char data[BUFF_SIZE]; int data_len; + int fd; struct receive_list_entry_s *next; }; typedef struct receive_list_entry_s receive_list_entry_t; @@ -147,16 +189,6 @@ typedef struct receive_list_entry_s receive_list_entry_t; /* * Private variables */ -static const char *config_keys[] = -{ - "CacheFlush", - "Listen", - "Server", - "TimeToLive", - "Forward" -}; -static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); - static int network_config_ttl = 0; static int network_config_forward = 0; @@ -167,20 +199,29 @@ static receive_list_entry_t *receive_list_tail = NULL; static pthread_mutex_t receive_list_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t receive_list_cond = PTHREAD_COND_INITIALIZER; -static struct pollfd *listen_sockets = NULL; -static int listen_sockets_num = 0; - -static int listen_loop = 0; -static pthread_t receive_thread_id = 0; -static pthread_t dispatch_thread_id = 0; - -static char send_buffer[BUFF_SIZE]; -static char *send_buffer_ptr; -static int send_buffer_fill; -static value_list_t send_buffer_vl = VALUE_LIST_STATIC; -static pthread_mutex_t send_buffer_lock = PTHREAD_MUTEX_INITIALIZER; - -static c_avl_tree_t *cache_tree = NULL; +static sockent_t *listen_sockets = NULL; +static struct pollfd *listen_sockets_pollfd = NULL; +static int listen_sockets_num = 0; + +/* The receive and dispatch threads will run as long as `listen_loop' is set to + * zero. */ +static int listen_loop = 0; +static int receive_thread_running = 0; +static pthread_t receive_thread_id; +static int dispatch_thread_running = 0; +static pthread_t dispatch_thread_id; + +/* Buffer in which to-be-sent network packets are constructed. */ +static char send_buffer[BUFF_SIZE]; +static char *send_buffer_ptr; +static int send_buffer_fill; +static value_list_t send_buffer_vl = VALUE_LIST_STATIC; +static pthread_mutex_t send_buffer_lock = PTHREAD_MUTEX_INITIALIZER; + +/* In this cache we store all the values we received, so we can send out only + * those values which were *not* received via the network plugin, too. This is + * used for the `Forward false' option. */ +static c_avl_tree_t *cache_tree = NULL; static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER; static time_t cache_flush_last = 0; static int cache_flush_interval = 1800; @@ -296,9 +337,6 @@ static int cache_check (const value_list_t *vl) pthread_mutex_unlock (&cache_lock); - DEBUG ("network plugin: cache_check: key = %s; time = %i; retval = %i", - key, (int) vl->time, retval); - return (retval); } /* int cache_check */ @@ -673,15 +711,161 @@ static int parse_part_string (void **ret_buffer, int *ret_buffer_len, return (0); } /* int parse_part_string */ -static int parse_packet (void *buffer, int buffer_len) +#if HAVE_GCRYPT_H +static int parse_part_sign_sha256 (sockent_t *se, /* {{{ */ + void **ret_buffer, int *ret_buffer_len) +{ + char *buffer = *ret_buffer; + size_t buffer_len = (size_t) *ret_buffer_len; + + part_signature_sha256_t ps_received; + part_signature_sha256_t ps_expected; + + if (se->shared_secret == NULL) + { + NOTICE ("network plugin: Received signed network packet but can't verify " + "it because no shared secret has been configured. Will accept it."); + return (0); + } + + if (buffer_len < sizeof (ps_received)) + return (-ENOMEM); + + memcpy (&ps_received, buffer, sizeof (ps_received)); + + memset (&ps_expected, 0, sizeof (ps_expected)); + ps_expected.head.type = htons (TYPE_SIGN_SHA256); + ps_expected.head.length = htons (sizeof (ps_expected)); + sstrncpy (ps_expected.hash, se->shared_secret, sizeof (ps_expected.hash)); + memcpy (buffer, &ps_expected, sizeof (ps_expected)); + + gcry_md_hash_buffer (GCRY_MD_SHA256, ps_expected.hash, buffer, buffer_len); + + *ret_buffer += sizeof (ps_received); + + if (memcmp (ps_received.hash, ps_expected.hash, + sizeof (ps_received.hash)) == 0) + return (0); + else /* hashes do not match. */ + return (1); +} /* }}} int parse_part_sign_sha256 */ +/* #endif HAVE_GCRYPT_H */ + +#else /* if !HAVE_GCRYPT_H */ +static int parse_part_sign_sha256 (sockent_t *se, /* {{{ */ + void **ret_buffer, int *ret_buffer_len) +{ + INFO ("network plugin: Received signed packet, but the network " + "plugin was not linked with libgcrypt, so I cannot " + "verify the signature. The packet will be accepted."); + return (0); +} /* }}} int parse_part_sign_sha256 */ +#endif /* !HAVE_GCRYPT_H */ + +#if HAVE_GCRYPT_H +static int parse_part_encr_aes256 (sockent_t *se, /* {{{ */ + void **ret_buffer, int *ret_buffer_len) +{ + char *buffer = *ret_buffer; + int buffer_len = *ret_buffer_len; + int orig_buffer_len; + part_encryption_aes256_t pea; + char hash[28]; + gcry_error_t err; + + if (se->cypher == NULL) + { + NOTICE ("network plugin: Unable to decrypt packet, because no cypher " + "instance is present."); + return (-1); + } + + /* Decrypt the packet in-place */ + err = gcry_cipher_decrypt (se->cypher, + buffer + sizeof (pea.head), buffer_len - sizeof (pea.head), + /* in = */ NULL, /* in len = */ 0); + gcry_cipher_reset (se->cypher); + if (err != 0) + { + ERROR ("network plugin: gcry_cipher_decrypt returned: %s", + gcry_strerror (err)); + return (-1); + } + + /* Copy the header information to `pea' */ + memcpy (&pea, buffer, sizeof (pea)); + buffer += sizeof (pea); + buffer_len -= sizeof (pea); + + /* Check sanity of the original length */ + orig_buffer_len = ntohs (pea.orig_length); + if (orig_buffer_len > buffer_len) + { + ERROR ("network plugin: Decryption failed: Invalid original length."); + return (-1); + } + + /* Check hash sum */ + memset (hash, 0, sizeof (hash)); + gcry_md_hash_buffer (GCRY_MD_SHA224, hash, buffer, orig_buffer_len); + + if (memcmp (hash, pea.hash, sizeof (hash)) != 0) + { + ERROR ("network plugin: Decryption failed: Checksum mismatch."); + return (-1); + } + + /* Update return values */ + *ret_buffer = buffer; + *ret_buffer_len = orig_buffer_len; + + return (0); +} /* }}} int parse_part_encr_aes256 */ +/* #endif HAVE_GCRYPT_H */ + +#else /* if !HAVE_GCRYPT_H */ +static int parse_part_encr_aes256 (sockent_t *se, /* {{{ */ + void **ret_buffer, int *ret_buffer_len) +{ + INFO ("network plugin: Received encrypted packet, but the network " + "plugin was not linked with libgcrypt, so I cannot " + "decrypt it. The packet will be discarded."); + return (-1); +} /* }}} int parse_part_encr_aes256 */ +#endif /* !HAVE_GCRYPT_H */ + +static int parse_packet (receive_list_entry_t *rle) /* {{{ */ { int status; + void *buffer; + int buffer_len; + sockent_t *se; + value_list_t vl = VALUE_LIST_INIT; notification_t n; - DEBUG ("network plugin: parse_packet: buffer = %p; buffer_len = %i;", - buffer, buffer_len); + int packet_was_encrypted = 0; + int packet_was_signed = 0; +#if HAVE_GCRYPT_H + int printed_ignore_warning = 0; +#endif /* HAVE_GCRYPT_H */ + + buffer = rle->data; + buffer_len = rle->data_len; + + /* Look for the correct `sockent_t' */ + se = listen_sockets; + while ((se != NULL) && (se->fd != rle->fd)) + se = se->next; + + if (se == NULL) + { + ERROR ("network plugin: Got packet from FD %i, but can't " + "find an appropriate socket entry.", + rle->fd); + return (-1); + } memset (&vl, '\0', sizeof (vl)); memset (&n, '\0', sizeof (n)); @@ -709,7 +893,72 @@ static int parse_packet (void *buffer, int buffer_len) if (pkg_length < (2 * sizeof (uint16_t))) break; - if (pkg_type == TYPE_VALUES) + if (pkg_type == TYPE_ENCR_AES256) + { + status = parse_part_encr_aes256 (se, &buffer, &buffer_len); + if (status != 0) + { + ERROR ("network plugin: Decrypting AES256 " + "part failed " + "with status %i.", status); + break; + } + else + { + packet_was_encrypted = 1; + } + } +#if HAVE_GCRYPT_H + else if ((se->security_level == SECURITY_LEVEL_ENCRYPT) + && (packet_was_encrypted == 0)) + { + if (printed_ignore_warning == 0) + { + INFO ("network plugin: Unencrypted packet or " + "part has been ignored."); + printed_ignore_warning = 1; + } + buffer = ((char *) buffer) + pkg_length; + continue; + } +#endif /* HAVE_GCRYPT_H */ + else if (pkg_type == TYPE_SIGN_SHA256) + { + status = parse_part_sign_sha256 (se, &buffer, &buffer_len); + if (status < 0) + { + ERROR ("network plugin: Verifying SHA-256 " + "signature failed " + "with status %i.", status); + break; + } + else if (status > 0) + { + ERROR ("network plugin: Ignoring packet with " + "invalid SHA-256 signature."); + break; + } + else + { + packet_was_signed = 1; + } + } +#if HAVE_GCRYPT_H + else if ((se->security_level == SECURITY_LEVEL_SIGN) + && (packet_was_encrypted == 0) + && (packet_was_signed == 0)) + { + if (printed_ignore_warning == 0) + { + INFO ("network plugin: Unsigned packet or " + "part has been ignored."); + printed_ignore_warning = 1; + } + buffer = ((char *) buffer) + pkg_length; + continue; + } +#endif /* HAVE_GCRYPT_H */ + else if (pkg_type == TYPE_VALUES) { status = parse_part_values (&buffer, &buffer_len, &vl.values, &vl.values_len); @@ -845,19 +1094,30 @@ static int parse_packet (void *buffer, int buffer_len) } /* while (buffer_len > sizeof (part_header_t)) */ return (status); -} /* int parse_packet */ +} /* }}} int parse_packet */ -static void free_sockent (sockent_t *se) +static void free_sockent (sockent_t *se) /* {{{ */ { sockent_t *next; while (se != NULL) { next = se->next; + +#if HAVE_GCRYPT_H + if (se->cypher != NULL) + { + gcry_cipher_close (se->cypher); + se->cypher = NULL; + } + free (se->shared_secret); +#endif /* HAVE_GCRYPT_H */ + free (se->addr); free (se); + se = next; } -} /* void free_sockent */ +} /* }}} void free_sockent */ /* * int network_set_ttl @@ -922,6 +1182,50 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai) return (0); } /* int network_set_ttl */ +#if HAVE_GCRYPT_H +static int network_set_encryption (sockent_t *se, /* {{{ */ + const char *shared_secret) +{ + char hash[32]; + gcry_error_t err; + + se->shared_secret = sstrdup (shared_secret); + + /* + * We use CBC *without* an initialization vector: The cipher is reset after + * each packet and we would have to re-set the IV each time. The first + * encrypted block will contain the SHA-224 checksum anyway, so this should + * be quite unpredictable. Also, there's a 2 byte field in the header that's + * being filled with random numbers. So we only use CBC so the blocks + * *within* one packet are chained. + */ + err = gcry_cipher_open (&se->cypher, + GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, /* flags = */ 0); + if (err != 0) + { + ERROR ("network plugin: gcry_cipher_open returned: %s", + gcry_strerror (err)); + return (-1); + } + + assert (se->shared_secret != NULL); + gcry_md_hash_buffer (GCRY_MD_SHA256, hash, + se->shared_secret, strlen (se->shared_secret)); + + err = gcry_cipher_setkey (se->cypher, hash, sizeof (hash)); + if (err != 0) + { + DEBUG ("network plugin: gcry_cipher_setkey returned: %s", + gcry_strerror (err)); + gcry_cipher_close (se->cypher); + se->cypher = NULL; + return (-1); + } + + return (0); +} /* }}} int network_set_encryption */ +#endif /* HAVE_GCRYPT_H */ + static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai) { int loop = 0; @@ -1029,9 +1333,12 @@ static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai) return (0); } /* int network_bind_socket */ -static sockent_t *network_create_socket (const char *node, +#define CREATE_SOCKET_FLAGS_LISTEN 0x0001 +static sockent_t *network_create_socket (const char *node, /* {{{ */ const char *service, - int listen) + const char *shared_secret, + int security_level, + int flags) { struct addrinfo ai_hints; struct addrinfo *ai_list, *ai_ptr; @@ -1070,6 +1377,7 @@ static sockent_t *network_create_socket (const char *node, for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) { sockent_t *se; + int status; if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL) { @@ -1111,9 +1419,10 @@ static sockent_t *network_create_socket (const char *node, continue; } - if (listen != 0) + if ((flags & CREATE_SOCKET_FLAGS_LISTEN) != 0) { - if (network_bind_socket (se, ai_ptr) != 0) + status = network_bind_socket (se, ai_ptr); + if (status != 0) { close (se->fd); free (se->addr); @@ -1121,11 +1430,45 @@ static sockent_t *network_create_socket (const char *node, continue; } } - else /* listen == 0 */ + else /* sending socket */ { network_set_ttl (se, ai_ptr); } +#if HAVE_GCRYPT_H + se->security_level = security_level; + se->shared_secret = NULL; + se->cypher = NULL; + if (shared_secret != NULL) + { + status = network_set_encryption (se, shared_secret); + if ((status != 0) && (security_level <= SECURITY_LEVEL_SIGN)) + { + WARNING ("network plugin: Starting cryptograp" + "hic subsystem failed. Since " + "security level `Sign' or " + "`None' is configured I will " + "continue."); + } + else if (status != 0) + { + ERROR ("network plugin: Starting cryptograp" + "hic subsystem failed. " + "Because the security level " + "is set to `Encrypt' I will " + "not continue!"); + close (se->fd); + free (se->addr); + free (se); + continue; + } + } /* if (shared_secret != NULL) */ +#else + /* Make compiler happy */ + security_level = 0; + shared_secret = NULL; +#endif /* HAVE_GCRYPT_H */ + if (se_tail == NULL) { se_head = se; @@ -1138,26 +1481,27 @@ static sockent_t *network_create_socket (const char *node, } /* We don't open more than one write-socket per node/service pair.. */ - if (listen == 0) + if ((flags & CREATE_SOCKET_FLAGS_LISTEN) == 0) break; } freeaddrinfo (ai_list); return (se_head); -} /* sockent_t *network_create_socket */ +} /* }}} sockent_t *network_create_socket */ -static sockent_t *network_create_default_socket (int listen) +static sockent_t *network_create_default_socket (int flags) /* {{{ */ { sockent_t *se_ptr = NULL; sockent_t *se_head = NULL; sockent_t *se_tail = NULL; - se_ptr = network_create_socket (NET_DEFAULT_V6_ADDR, - NET_DEFAULT_PORT, listen); + se_ptr = network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT, + /* shared secret = */ NULL, SECURITY_LEVEL_NONE, + flags); /* Don't send to the same machine in IPv6 and IPv4 if both are available. */ - if ((listen == 0) && (se_ptr != NULL)) + if (((flags & CREATE_SOCKET_FLAGS_LISTEN) == 0) && (se_ptr != NULL)) return (se_ptr); if (se_ptr != NULL) @@ -1168,28 +1512,36 @@ static sockent_t *network_create_default_socket (int listen) se_tail = se_tail->next; } - se_ptr = network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT, listen); + se_ptr = network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT, + /* shared secret = */ NULL, SECURITY_LEVEL_NONE, + flags); if (se_tail == NULL) return (se_ptr); se_tail->next = se_ptr; return (se_head); -} /* sockent_t *network_create_default_socket */ +} /* }}} sockent_t *network_create_default_socket */ -static int network_add_listen_socket (const char *node, const char *service) +static int network_add_listen_socket (const char *node, /* {{{ */ + const char *service, const char *shared_secret, int security_level) { sockent_t *se; sockent_t *se_ptr; int se_num = 0; + int flags; + + flags = CREATE_SOCKET_FLAGS_LISTEN; + if (service == NULL) service = NET_DEFAULT_PORT; if (node == NULL) - se = network_create_default_socket (1 /* listen == true */); + se = network_create_default_socket (flags); else - se = network_create_socket (node, service, 1 /* listen == true */); + se = network_create_socket (node, service, + shared_secret, security_level, flags); if (se == NULL) return (-1); @@ -1197,23 +1549,32 @@ static int network_add_listen_socket (const char *node, const char *service) for (se_ptr = se; se_ptr != NULL; se_ptr = se_ptr->next) se_num++; - listen_sockets = (struct pollfd *) realloc (listen_sockets, + listen_sockets_pollfd = realloc (listen_sockets_pollfd, (listen_sockets_num + se_num) * sizeof (struct pollfd)); for (se_ptr = se; se_ptr != NULL; se_ptr = se_ptr->next) { - listen_sockets[listen_sockets_num].fd = se_ptr->fd; - listen_sockets[listen_sockets_num].events = POLLIN | POLLPRI; - listen_sockets[listen_sockets_num].revents = 0; + listen_sockets_pollfd[listen_sockets_num].fd = se_ptr->fd; + listen_sockets_pollfd[listen_sockets_num].events = POLLIN | POLLPRI; + listen_sockets_pollfd[listen_sockets_num].revents = 0; listen_sockets_num++; } /* for (se) */ - free_sockent (se); + se_ptr = listen_sockets; + while ((se_ptr != NULL) && (se_ptr->next != NULL)) + se_ptr = se_ptr->next; + + if (se_ptr == NULL) + listen_sockets = se; + else + se_ptr->next = se; + return (0); -} /* int network_add_listen_socket */ +} /* }}} int network_add_listen_socket */ -static int network_add_sending_socket (const char *node, const char *service) +static int network_add_sending_socket (const char *node, /* {{{ */ + const char *service, const char *shared_secret, int security_level) { sockent_t *se; sockent_t *se_ptr; @@ -1222,9 +1583,11 @@ static int network_add_sending_socket (const char *node, const char *service) service = NET_DEFAULT_PORT; if (node == NULL) - se = network_create_default_socket (0 /* listen == false */); + se = network_create_default_socket (/* flags = */ 0); else - se = network_create_socket (node, service, 0 /* listen == false */); + se = network_create_socket (node, service, + shared_secret, security_level, + /* flags = */ 0); if (se == NULL) return (-1); @@ -1240,7 +1603,7 @@ static int network_add_sending_socket (const char *node, const char *service) se_ptr->next = se; return (0); -} /* int network_get_listen_socket */ +} /* }}} int network_add_sending_socket */ static void *dispatch_thread (void __attribute__((unused)) *arg) { @@ -1265,13 +1628,13 @@ static void *dispatch_thread (void __attribute__((unused)) *arg) if (ent == NULL) break; - parse_packet (ent->data, ent->data_len); + parse_packet (ent); sfree (ent); } /* while (42) */ return (NULL); -} /* void *receive_thread */ +} /* void *dispatch_thread */ static int network_receive (void) { @@ -1285,7 +1648,10 @@ static int network_receive (void) receive_list_entry_t *private_list_tail; if (listen_sockets_num == 0) - network_add_listen_socket (NULL, NULL); + network_add_listen_socket (/* node = */ NULL, + /* service = */ NULL, + /* shared secret = */ NULL, + /* encryption = */ 0); if (listen_sockets_num == 0) { @@ -1298,7 +1664,7 @@ static int network_receive (void) while (listen_loop == 0) { - status = poll (listen_sockets, listen_sockets_num, -1); + status = poll (listen_sockets_pollfd, listen_sockets_num, -1); if (status <= 0) { @@ -1314,11 +1680,12 @@ static int network_receive (void) { receive_list_entry_t *ent; - if ((listen_sockets[i].revents & (POLLIN | POLLPRI)) == 0) + if ((listen_sockets_pollfd[i].revents + & (POLLIN | POLLPRI)) == 0) continue; status--; - buffer_len = recv (listen_sockets[i].fd, + buffer_len = recv (listen_sockets_pollfd[i].fd, buffer, sizeof (buffer), 0 /* no flags */); if (buffer_len < 0) @@ -1330,6 +1697,10 @@ static int network_receive (void) return (-1); } + /* TODO: Possible performance enhancement: Do not free + * these entries in the dispatch thread but put them in + * another list, so we don't have to allocate more and + * more of these structures. */ ent = malloc (sizeof (receive_list_entry_t)); if (ent == NULL) { @@ -1337,6 +1708,7 @@ static int network_receive (void) return (-1); } memset (ent, 0, sizeof (receive_list_entry_t)); + ent->fd = listen_sockets_pollfd[i].fd; ent->next = NULL; /* Hopefully this be optimized out by the compiler. It @@ -1369,7 +1741,7 @@ static int network_receive (void) pthread_cond_signal (&receive_list_cond); pthread_mutex_unlock (&receive_list_lock); } - } /* for (listen_sockets) */ + } /* for (listen_sockets_pollfd) */ } /* while (listen_loop == 0) */ /* Make sure everything is dispatched before exiting. */ @@ -1391,43 +1763,146 @@ static int network_receive (void) } return (0); -} +} /* int network_receive */ static void *receive_thread (void __attribute__((unused)) *arg) { return (network_receive () ? (void *) 1 : (void *) 0); } /* void *receive_thread */ -static void network_send_buffer (const char *buffer, int buffer_len) +static void network_init_buffer (void) { - sockent_t *se; - int status; + memset (send_buffer, 0, sizeof (send_buffer)); + send_buffer_ptr = send_buffer; + send_buffer_fill = 0; - DEBUG ("network plugin: network_send_buffer: buffer_len = %i", buffer_len); + memset (&send_buffer_vl, 0, sizeof (send_buffer_vl)); +} /* int network_init_buffer */ - for (se = sending_sockets; se != NULL; se = se->next) +static void networt_send_buffer_plain (const sockent_t *se, /* {{{ */ + const char *buffer, size_t buffer_size) +{ + int status; + + while (42) { - while (42) + status = sendto (se->fd, buffer, buffer_size, 0 /* no flags */, + (struct sockaddr *) se->addr, se->addrlen); + if (status < 0) { - status = sendto (se->fd, buffer, buffer_len, 0 /* no flags */, - (struct sockaddr *) se->addr, se->addrlen); - if (status < 0) - { - char errbuf[1024]; - if (errno == EINTR) - continue; - ERROR ("network plugin: sendto failed: %s", - sstrerror (errno, errbuf, - sizeof (errbuf))); - break; - } - + char errbuf[1024]; + if (errno == EINTR) + continue; + ERROR ("network plugin: sendto failed: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); break; - } /* while (42) */ - } /* for (sending_sockets) */ -} /* void network_send_buffer */ + } + + break; + } /* while (42) */ +} /* }}} void networt_send_buffer_plain */ + +#if HAVE_GCRYPT_H +static void networt_send_buffer_signed (const sockent_t *se, /* {{{ */ + const char *in_buffer, size_t in_buffer_size) +{ + part_signature_sha256_t ps; + char buffer[sizeof (ps) + in_buffer_size]; + char hash[sizeof (ps.hash)]; + + /* Initialize the `ps' structure. */ + memset (&ps, 0, sizeof (ps)); + ps.head.type = htons (TYPE_SIGN_SHA256); + ps.head.length = htons ((uint16_t) sizeof (ps)); + sstrncpy (ps.hash, se->shared_secret, sizeof (ps.hash)); + + /* Prepend the signature. */ + memcpy (buffer, &ps, sizeof (ps)); + memcpy (buffer + sizeof (ps), in_buffer, in_buffer_size); + + /* Calculate the hash value. */ + gcry_md_hash_buffer (GCRY_MD_SHA256, hash, buffer, sizeof (buffer)); + + /* Copy the hash value into the buffer. */ + memcpy (ps.hash, hash, sizeof (ps.hash)); + memcpy (buffer, &ps, sizeof (ps)); + + networt_send_buffer_plain (se, buffer, sizeof (buffer)); +} /* }}} void networt_send_buffer_signed */ + +static void networt_send_buffer_encrypted (const sockent_t *se, /* {{{ */ + const char *in_buffer, size_t in_buffer_size) +{ + part_encryption_aes256_t pea; + char buffer[sizeof (pea) + in_buffer_size + 16]; + size_t buffer_size; + gcry_error_t err; + + /* Round to the next multiple of 16, because AES has a block size of 128 bit. + * the first four bytes of `pea' are not encrypted and must be subtracted. */ + buffer_size = (sizeof (pea) + in_buffer_size + 15 - sizeof (pea.head)) / 16; + buffer_size = buffer_size * 16; + buffer_size += sizeof (pea.head); + + DEBUG ("network plugin: networt_send_buffer_encrypted: " + "buffer_size = %zu;", buffer_size); + + /* Initialize the header fields */ + memset (&pea, 0, sizeof (pea)); + pea.head.type = htons (TYPE_ENCR_AES256); + pea.head.length = htons ((uint16_t) buffer_size); + pea.orig_length = htons ((uint16_t) in_buffer_size); + + /* Fill the extra field with random values. Some entropy in the encrypted + * data is usually not a bad thing, I hope. */ + gcry_randomize (&pea.random, sizeof (pea.random), GCRY_STRONG_RANDOM); + + /* Create hash of the payload */ + gcry_md_hash_buffer (GCRY_MD_SHA224, pea.hash, in_buffer, in_buffer_size); + + /* Initialize the buffer */ + memset (buffer, 0, sizeof (buffer)); + memcpy (buffer, &pea, sizeof (pea)); + memcpy (buffer + sizeof (pea), in_buffer, in_buffer_size); + + /* Encrypt the buffer in-place */ + err = gcry_cipher_encrypt (se->cypher, + buffer + sizeof (pea.head), buffer_size - sizeof (pea.head), + /* in = */ NULL, /* in len = */ 0); + gcry_cipher_reset (se->cypher); + if (err != 0) + { + ERROR ("network plugin: gcry_cipher_encrypt returned: %s", + gcry_strerror (err)); + return; + } + + /* Send it out without further modifications */ + networt_send_buffer_plain (se, buffer, buffer_size); +} /* }}} void networt_send_buffer_encrypted */ +#endif /* HAVE_GCRYPT_H */ + +static void network_send_buffer (char *buffer, size_t buffer_len) /* {{{ */ +{ + sockent_t *se; -static int add_to_buffer (char *buffer, int buffer_size, + DEBUG ("network plugin: network_send_buffer: buffer_len = %zu", buffer_len); + + for (se = sending_sockets; se != NULL; se = se->next) + { +#if HAVE_GCRYPT_H + if (se->security_level == SECURITY_LEVEL_ENCRYPT) + networt_send_buffer_encrypted (se, buffer, buffer_len); + else if (se->security_level == SECURITY_LEVEL_SIGN) + networt_send_buffer_signed (se, buffer, buffer_len); + else /* if (se->security_level == SECURITY_LEVEL_NONE) */ +#endif /* HAVE_GCRYPT_H */ + networt_send_buffer_plain (se, buffer, buffer_len); + } /* for (sending_sockets) */ +} /* }}} void network_send_buffer */ + +static int add_to_buffer (char *buffer, int buffer_size, /* {{{ */ value_list_t *vl_def, const data_set_t *ds, const value_list_t *vl) { @@ -1495,20 +1970,19 @@ static int add_to_buffer (char *buffer, int buffer_size, return (-1); return (buffer - buffer_orig); -} /* int add_to_buffer */ +} /* }}} int add_to_buffer */ static void flush_buffer (void) { DEBUG ("network plugin: flush_buffer: send_buffer_fill = %i", send_buffer_fill); - network_send_buffer (send_buffer, send_buffer_fill); - send_buffer_ptr = send_buffer; - send_buffer_fill = 0; - memset (&send_buffer_vl, 0, sizeof (send_buffer_vl)); + network_send_buffer (send_buffer, (size_t) send_buffer_fill); + network_init_buffer (); } -static int network_write (const data_set_t *ds, const value_list_t *vl) +static int network_write (const data_set_t *ds, const value_list_t *vl, + user_data_t __attribute__((unused)) *user_data) { int status; @@ -1523,7 +1997,7 @@ static int network_write (const data_set_t *ds, const value_list_t *vl) pthread_mutex_lock (&send_buffer_lock); status = add_to_buffer (send_buffer_ptr, - sizeof (send_buffer) - send_buffer_fill, + sizeof (send_buffer) - (send_buffer_fill + BUFF_SIG_SIZE), &send_buffer_vl, ds, vl); if (status >= 0) @@ -1537,7 +2011,7 @@ static int network_write (const data_set_t *ds, const value_list_t *vl) flush_buffer (); status = add_to_buffer (send_buffer_ptr, - sizeof (send_buffer) - send_buffer_fill, + sizeof (send_buffer) - (send_buffer_fill + BUFF_SIG_SIZE), &send_buffer_vl, ds, vl); @@ -1563,76 +2037,207 @@ static int network_write (const data_set_t *ds, const value_list_t *vl) return ((status < 0) ? -1 : 0); } /* int network_write */ -static int network_config (const char *key, const char *val) +static int network_config_set_boolean (const oconfig_item_t *ci, /* {{{ */ + int *retval) { - char *node; - char *service; + if ((ci->values_num != 1) + || ((ci->values[0].type != OCONFIG_TYPE_BOOLEAN) + && (ci->values[0].type != OCONFIG_TYPE_STRING))) + { + ERROR ("network plugin: The `%s' config option needs " + "exactly one boolean argument.", ci->key); + return (-1); + } - char *fields[3]; - int fields_num; + if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN) + { + if (ci->values[0].value.boolean) + *retval = 1; + else + *retval = 0; + } + else + { + char *str = ci->values[0].value.string; + + if ((strcasecmp ("true", str) == 0) + || (strcasecmp ("yes", str) == 0) + || (strcasecmp ("on", str) == 0)) + *retval = 1; + else if ((strcasecmp ("false", str) == 0) + || (strcasecmp ("no", str) == 0) + || (strcasecmp ("off", str) == 0)) + *retval = 0; + else + { + ERROR ("network plugin: Cannot parse string value `%s' of the `%s' " + "option as boolean value.", + str, ci->key); + return (-1); + } + } - if ((strcasecmp ("Listen", key) == 0) - || (strcasecmp ("Server", key) == 0)) - { - char *val_cpy = strdup (val); - if (val_cpy == NULL) - return (1); + return (0); +} /* }}} int network_config_set_boolean */ - service = NET_DEFAULT_PORT; - fields_num = strsplit (val_cpy, fields, 3); - if ((fields_num != 1) - && (fields_num != 2)) - { - sfree (val_cpy); - return (1); - } - else if (fields_num == 2) - { - if ((service = strchr (fields[1], '.')) != NULL) - *service = '\0'; - service = fields[1]; - } - node = fields[0]; +static int network_config_set_ttl (const oconfig_item_t *ci) /* {{{ */ +{ + int tmp; + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) + { + WARNING ("network plugin: The `TimeToLive' config option needs exactly " + "one numeric argument."); + return (-1); + } - if (strcasecmp ("Listen", key) == 0) - network_add_listen_socket (node, service); - else - network_add_sending_socket (node, service); + tmp = (int) ci->values[0].value.number; + if ((tmp > 0) && (tmp <= 255)) + network_config_ttl = tmp; - sfree (val_cpy); - } - else if (strcasecmp ("TimeToLive", key) == 0) - { - int tmp = atoi (val); - if ((tmp > 0) && (tmp < 256)) - network_config_ttl = tmp; - else - return (1); - } - else if (strcasecmp ("Forward", key) == 0) - { - if ((strcasecmp ("true", val) == 0) - || (strcasecmp ("yes", val) == 0) - || (strcasecmp ("on", val) == 0)) - network_config_forward = 1; - else - network_config_forward = 0; - } - else if (strcasecmp ("CacheFlush", key) == 0) - { - int tmp = atoi (val); - if (tmp > 0) - cache_flush_interval = tmp; - else return (1); - } - else - { - return (-1); - } - return (0); -} /* int network_config */ + return (0); +} /* }}} int network_config_set_ttl */ + +#if HAVE_GCRYPT_H +static int network_config_set_security_level (oconfig_item_t *ci, /* {{{ */ + int *retval) +{ + char *str; + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("network plugin: The `SecurityLevel' config option needs exactly " + "one string argument."); + return (-1); + } + + str = ci->values[0].value.string; + if (strcasecmp ("Encrypt", str) == 0) + *retval = SECURITY_LEVEL_ENCRYPT; + else if (strcasecmp ("Sign", str) == 0) + *retval = SECURITY_LEVEL_SIGN; + else if (strcasecmp ("None", str) == 0) + *retval = SECURITY_LEVEL_NONE; + else + { + WARNING ("network plugin: Unknown security level: %s.", str); + return (-1); + } + + return (0); +} /* }}} int network_config_set_security_level */ +#endif /* HAVE_GCRYPT_H */ + +static int network_config_listen_server (const oconfig_item_t *ci) /* {{{ */ +{ + char *node; + char *service; + char *shared_secret = NULL; + int security_level = SECURITY_LEVEL_NONE; + int i; + + if ((ci->values_num < 1) || (ci->values_num > 2) + || (ci->values[0].type != OCONFIG_TYPE_STRING) + || ((ci->values_num > 1) && (ci->values[1].type != OCONFIG_TYPE_STRING))) + { + ERROR ("network plugin: The `%s' config option needs " + "one or two string arguments.", ci->key); + return (-1); + } + + node = ci->values[0].value.string; + if (ci->values_num >= 2) + service = ci->values[1].value.string; + else + service = NULL; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + +#if HAVE_GCRYPT_H + if (strcasecmp ("Secret", child->key) == 0) + { + if ((child->values_num == 1) + && (child->values[0].type == OCONFIG_TYPE_STRING)) + shared_secret = child->values[0].value.string; + else + ERROR ("network plugin: The `Secret' option needs exactly one string " + "argument."); + } + else if (strcasecmp ("SecurityLevel", child->key) == 0) + network_config_set_security_level (child, &security_level); + else +#endif /* HAVE_GCRYPT_H */ + { + WARNING ("network plugin: Option `%s' is not allowed here.", + child->key); + } + } + + if ((security_level > SECURITY_LEVEL_NONE) && (shared_secret == NULL)) + { + ERROR ("network plugin: A security level higher than `none' was " + "requested, but no shared key was given. Cowardly refusing to open " + "this socket!"); + return (-1); + } + + if (strcasecmp ("Listen", ci->key) == 0) + network_add_listen_socket (node, service, shared_secret, security_level); + else + network_add_sending_socket (node, service, shared_secret, security_level); + + return (0); +} /* }}} int network_config_listen_server */ + +static int network_config_set_cache_flush (const oconfig_item_t *ci) /* {{{ */ +{ + int tmp; + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) + { + WARNING ("network plugin: The `CacheFlush' config option needs exactly " + "one numeric argument."); + return (-1); + } -static int network_notification (const notification_t *n) + tmp = (int) ci->values[0].value.number; + if (tmp > 0) + network_config_ttl = tmp; + + return (0); +} /* }}} int network_config_set_cache_flush */ + +static int network_config (oconfig_item_t *ci) /* {{{ */ +{ + int i; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if ((strcasecmp ("Listen", child->key) == 0) + || (strcasecmp ("Server", child->key) == 0)) + network_config_listen_server (child); + else if (strcasecmp ("TimeToLive", child->key) == 0) + network_config_set_ttl (child); + else if (strcasecmp ("Forward", child->key) == 0) + network_config_set_boolean (child, &network_config_forward); + else if (strcasecmp ("CacheFlush", child->key) == 0) + network_config_set_cache_flush (child); + else + { + WARNING ("network plugin: Option `%s' is not allowed here.", + child->key); + } + } + + return (0); +} /* }}} int network_config */ + +static int network_notification (const notification_t *n, + user_data_t __attribute__((unused)) *user_data) { char buffer[BUFF_SIZE]; char *buffer_ptr = buffer; @@ -1708,16 +2313,27 @@ static int network_shutdown (void) listen_loop++; /* Kill the listening thread */ - if (receive_thread_id != (pthread_t) 0) + if (receive_thread_running != 0) { + INFO ("network plugin: Stopping receive thread."); pthread_kill (receive_thread_id, SIGTERM); pthread_join (receive_thread_id, NULL /* no return value */); - receive_thread_id = (pthread_t) 0; + memset (&receive_thread_id, 0, sizeof (receive_thread_id)); + receive_thread_running = 0; } /* Shutdown the dispatching thread */ - if (dispatch_thread_id != (pthread_t) 0) + if (dispatch_thread_running != 0) + { + INFO ("network plugin: Stopping dispatch thread."); + pthread_mutex_lock (&receive_list_lock); pthread_cond_broadcast (&receive_list_cond); + pthread_mutex_unlock (&receive_list_lock); + pthread_join (dispatch_thread_id, /* ret = */ NULL); + dispatch_thread_running = 0; + } + + free_sockent (listen_sockets); if (send_buffer_fill > 0) flush_buffer (); @@ -1758,9 +2374,7 @@ static int network_init (void) plugin_register_shutdown ("network", network_shutdown); - send_buffer_ptr = send_buffer; - send_buffer_fill = 0; - memset (&send_buffer_vl, 0, sizeof (send_buffer_vl)); + network_init_buffer (); cache_tree = c_avl_create ((int (*) (const void *, const void *)) strcmp); cache_flush_last = time (NULL); @@ -1768,14 +2382,21 @@ static int network_init (void) /* setup socket(s) and so on */ if (sending_sockets != NULL) { - plugin_register_write ("network", network_write); - plugin_register_notification ("network", network_notification); + plugin_register_write ("network", network_write, + /* user_data = */ NULL); + plugin_register_notification ("network", network_notification, + /* user_data = */ NULL); } - if ((listen_sockets_num != 0) && (receive_thread_id == 0)) + /* If no threads need to be started, return here. */ + if ((listen_sockets_num == 0) + || ((dispatch_thread_running != 0) + && (receive_thread_running != 0))) + return (0); + + if (dispatch_thread_running == 0) { int status; - status = pthread_create (&dispatch_thread_id, NULL /* no attributes */, dispatch_thread, @@ -1787,7 +2408,15 @@ static int network_init (void) sstrerror (errno, errbuf, sizeof (errbuf))); } + else + { + dispatch_thread_running = 1; + } + } + if (receive_thread_running == 0) + { + int status; status = pthread_create (&receive_thread_id, NULL /* no attributes */, receive_thread, @@ -1799,7 +2428,12 @@ static int network_init (void) sstrerror (errno, errbuf, sizeof (errbuf))); } + else + { + receive_thread_running = 1; + } } + return (0); } /* int network_init */ @@ -1811,7 +2445,8 @@ static int network_init (void) * there, good. If not, well, then there is nothing to flush.. -octo */ static int network_flush (int timeout, - const char __attribute__((unused)) *identifier) + const char __attribute__((unused)) *identifier, + user_data_t __attribute__((unused)) *user_data) { pthread_mutex_lock (&send_buffer_lock); @@ -1828,8 +2463,10 @@ static int network_flush (int timeout, void module_register (void) { - plugin_register_config ("network", network_config, - config_keys, config_keys_num); + plugin_register_complex_config ("network", network_config); plugin_register_init ("network", network_init); - plugin_register_flush ("network", network_flush); + plugin_register_flush ("network", network_flush, + /* user_data = */ NULL); } /* void module_register */ + +/* vim: set fdm=marker : */ diff --git a/src/network.h b/src/network.h index 4318ef92..777616c4 100644 --- a/src/network.h +++ b/src/network.h @@ -67,4 +67,7 @@ #define TYPE_MESSAGE 0x0100 #define TYPE_SEVERITY 0x0101 +#define TYPE_SIGN_SHA256 0x0200 +#define TYPE_ENCR_AES256 0x0210 + #endif /* NETWORK_H */ diff --git a/src/notify_desktop.c b/src/notify_desktop.c index 2e62e88c..839bc610 100644 --- a/src/notify_desktop.c +++ b/src/notify_desktop.c @@ -72,7 +72,8 @@ static int c_notify_config (oconfig_item_t *ci) return 0; } /* c_notify_config */ -static int c_notify (const notification_t *n) +static int c_notify (const notification_t *n, + user_data_t __attribute__((unused)) *user_data) { NotifyNotification *notification = NULL; NotifyUrgency urgency = NOTIFY_URGENCY_LOW; @@ -145,7 +146,8 @@ static int c_notify_init (void) free (spec_version); } - plugin_register_notification ("notify_desktop", c_notify); + plugin_register_notification ("notify_desktop", c_notify, + /* user_data = */ NULL); plugin_register_shutdown ("notify_desktop", c_notify_shutdown); return 0; } /* c_notify_init */ diff --git a/src/notify_email.c b/src/notify_email.c index 32bd9164..62e1c486 100644 --- a/src/notify_email.c +++ b/src/notify_email.c @@ -203,7 +203,8 @@ static int notify_email_config (const char *key, const char *value) return 0; } /* int notify_email_config (const char *, const char *) */ -static int notify_email_notification (const notification_t *n) +static int notify_email_notification (const notification_t *n, + user_data_t __attribute__((unused)) *user_data) { smtp_recipient_t recipient; @@ -282,7 +283,8 @@ void module_register (void) plugin_register_shutdown ("notify_email", notify_email_shutdown); plugin_register_config ("notify_email", notify_email_config, config_keys, config_keys_num); - plugin_register_notification ("notify_email", notify_email_notification); + plugin_register_notification ("notify_email", notify_email_notification, + /* user_data = */ NULL); } /* void module_register (void) */ /* vim: set sw=2 sts=2 ts=8 et : */ diff --git a/src/onewire.c b/src/onewire.c index c40d5ad7..261457a1 100644 --- a/src/onewire.c +++ b/src/onewire.c @@ -59,12 +59,14 @@ static ow_family_features_t ow_family_features[] = static int ow_family_features_num = STATIC_ARRAY_SIZE (ow_family_features); static char *device_g = NULL; +static int ow_interval = 0; static const char *config_keys[] = { "Device", "IgnoreSelected", "Sensor", + "Interval" }; static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); @@ -104,33 +106,22 @@ static int cow_load_config (const char *key, const char *value) sfree (device_g); device_g = temp; } - else + else if (strcasecmp ("Interval", key) == 0) { - return (-1); + int tmp; + tmp = atoi (value); + if (tmp > 0) + ow_interval = tmp; + else + ERROR ("onewire plugin: Invalid `Interval' setting: %s", value); } - - return (0); -} - -static int cow_init (void) -{ - int status; - - if (device_g == NULL) + else { - ERROR ("onewire plugin: cow_init: No device configured."); return (-1); } - status = (int) OW_init (device_g); - if (status != 0) - { - ERROR ("onewire plugin: OW_init(%s) failed: %i.", device_g, status); - return (1); - } - return (0); -} /* int cow_init */ +} static int cow_read_values (const char *path, const char *name, const ow_family_features_t *family_info) @@ -287,7 +278,7 @@ static int cow_read_bus (const char *path) return (0); } /* int cow_read_bus */ -static int cow_read (void) +static int cow_read (user_data_t *ud __attribute__((unused))) { return (cow_read_bus ("/")); } /* int cow_read */ @@ -299,11 +290,38 @@ static int cow_shutdown (void) return (0); } /* int cow_shutdown */ +static int cow_init (void) +{ + int status; + struct timespec cb_interval; + + if (device_g == NULL) + { + ERROR ("onewire plugin: cow_init: No device configured."); + return (-1); + } + + status = (int) OW_init (device_g); + if (status != 0) + { + ERROR ("onewire plugin: OW_init(%s) failed: %i.", device_g, status); + return (1); + } + + memset (&cb_interval, 0, sizeof (cb_interval)); + if (ow_interval > 0) + cb_interval.tv_sec = (time_t) ow_interval; + + plugin_register_complex_read ("onewire", cow_read, + &cb_interval, /* user data = */ NULL); + plugin_register_shutdown ("onewire", cow_shutdown); + + return (0); +} /* int cow_init */ + void module_register (void) { plugin_register_init ("onewire", cow_init); - plugin_register_read ("onewire", cow_read); - plugin_register_shutdown ("onewire", cow_shutdown); plugin_register_config ("onewire", cow_load_config, config_keys, config_keys_num); } diff --git a/src/perl.c b/src/perl.c index 81ef7202..b6e7b22d 100644 --- a/src/perl.c +++ b/src/perl.c @@ -1527,6 +1527,9 @@ static XS (Collectd_plugin_register_ds) dXSARGS; + log_warn ("Using plugin_register() to register new data-sets is " + "deprecated - add new entries to a custom types.db instead."); + if (2 != items) { log_err ("Usage: Collectd::plugin_register_data_set(type, dataset)"); XSRETURN_EMPTY; @@ -1907,7 +1910,8 @@ static int perl_read (void) return pplugin_call_all (aTHX_ PLUGIN_READ); } /* static int perl_read (void) */ -static int perl_write (const data_set_t *ds, const value_list_t *vl) +static int perl_write (const data_set_t *ds, const value_list_t *vl, + user_data_t __attribute__((unused)) *user_data) { dTHX; @@ -1929,7 +1933,8 @@ static int perl_write (const data_set_t *ds, const value_list_t *vl) return pplugin_call_all (aTHX_ PLUGIN_WRITE, ds, vl); } /* static int perl_write (const data_set_t *, const value_list_t *) */ -static void perl_log (int level, const char *msg) +static void perl_log (int level, const char *msg, + user_data_t __attribute__((unused)) *user_data) { dTHX; @@ -1950,7 +1955,8 @@ static void perl_log (int level, const char *msg) return; } /* static void perl_log (int, const char *) */ -static int perl_notify (const notification_t *notif) +static int perl_notify (const notification_t *notif, + user_data_t __attribute__((unused)) *user_data) { dTHX; @@ -1969,7 +1975,8 @@ static int perl_notify (const notification_t *notif) return pplugin_call_all (aTHX_ PLUGIN_NOTIF, notif); } /* static int perl_notify (const notification_t *) */ -static int perl_flush (int timeout, const char *identifier) +static int perl_flush (int timeout, const char *identifier, + user_data_t __attribute__((unused)) *user_data) { dTHX; @@ -2215,14 +2222,15 @@ static int init_pi (int argc, char **argv) perl_run (aTHX); - plugin_register_log ("perl", perl_log); - plugin_register_notification ("perl", perl_notify); + plugin_register_log ("perl", perl_log, /* user_data = */ NULL); + plugin_register_notification ("perl", perl_notify, + /* user_data = */ NULL); plugin_register_init ("perl", perl_init); plugin_register_read ("perl", perl_read); - plugin_register_write ("perl", perl_write); - plugin_register_flush ("perl", perl_flush); + plugin_register_write ("perl", perl_write, /* user_data = */ NULL); + plugin_register_flush ("perl", perl_flush, /* user_data = */ NULL); plugin_register_shutdown ("perl", perl_shutdown); return 0; } /* static int init_pi (const char **, const int) */ diff --git a/src/ping.c b/src/ping.c index 7ffbfaff..de9c45bb 100644 --- a/src/ping.c +++ b/src/ping.c @@ -1,6 +1,6 @@ /** * collectd - src/ping.c - * Copyright (C) 2005,2006 Florian octo Forster + * Copyright (C) 2005-2009 Florian octo Forster * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -24,238 +24,553 @@ #include "plugin.h" #include "configfile.h" +#include #include -#include "liboping/oping.h" +#if HAVE_NETDB_H +# include /* NI_MAXHOST */ +#endif + +#include + +#ifndef NI_MAXHOST +# define NI_MAXHOST 1025 +#endif /* * Private data types */ struct hostlist_s { - char *host; - int wait_time; - int wait_left; - struct hostlist_s *next; + char *host; + + uint32_t pkg_sent; + uint32_t pkg_recv; + + double latency_total; + double latency_squared; + + struct hostlist_s *next; }; typedef struct hostlist_s hostlist_t; /* * Private variables */ -static pingobj_t *pingobj = NULL; -static hostlist_t *hosts = NULL; +static hostlist_t *hostlist_head = NULL; + +static int ping_ttl = PING_DEF_TTL; +static double ping_interval = 1.0; +static double ping_timeout = 0.9; + +static int ping_thread_loop = 0; +static int ping_thread_error = 0; +static pthread_t ping_thread_id; +static pthread_mutex_t ping_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t ping_cond = PTHREAD_COND_INITIALIZER; static const char *config_keys[] = { - "Host", - "TTL", - NULL + "Host", + "TTL", + "Interval", + "Timeout" }; -static int config_keys_num = 2; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); /* * Private functions */ -static void add_hosts (void) +/* Assure that `ts->tv_nsec' is in the range 0 .. 999999999 */ +static void time_normalize (struct timespec *ts) /* {{{ */ +{ + while (ts->tv_nsec < 0) + { + if (ts->tv_sec == 0) + { + ts->tv_nsec = 0; + return; + } + + ts->tv_sec -= 1; + ts->tv_nsec += 1000000000; + } + + while (ts->tv_nsec >= 1000000000) + { + ts->tv_sec += 1; + ts->tv_nsec -= 1000000000; + } +} /* }}} void time_normalize */ + +/* Add `ts_int' to `tv_begin' and store the result in `ts_dest'. If the result + * is larger than `tv_end', copy `tv_end' to `ts_dest' instead. */ +static void time_calc (struct timespec *ts_dest, /* {{{ */ + const struct timespec *ts_int, + const struct timeval *tv_begin, + const struct timeval *tv_end) +{ + ts_dest->tv_sec = tv_begin->tv_sec + ts_int->tv_sec; + ts_dest->tv_nsec = (tv_begin->tv_usec * 1000) + ts_int->tv_nsec; + time_normalize (ts_dest); + + /* Assure that `(begin + interval) > end'. + * This may seem overly complicated, but `tv_sec' is of type `time_t' + * which may be `unsigned. *sigh* */ + if ((tv_end->tv_sec > ts_dest->tv_sec) + || ((tv_end->tv_sec == ts_dest->tv_sec) + && ((tv_end->tv_usec * 1000) > ts_dest->tv_nsec))) + { + ts_dest->tv_sec = tv_end->tv_sec; + ts_dest->tv_nsec = 1000 * tv_end->tv_usec; + } + + time_normalize (ts_dest); +} /* }}} void time_calc */ + +static void *ping_thread (void *arg) /* {{{ */ { - hostlist_t *hl_this; - hostlist_t *hl_prev; - - hl_this = hosts; - hl_prev = NULL; - while (hl_this != NULL) - { - DEBUG ("ping plugin: host = %s, wait_left = %i, " - "wait_time = %i, next = %p", - hl_this->host, hl_this->wait_left, - hl_this->wait_time, (void *) hl_this->next); - - if (hl_this->wait_left <= 0) - { - if (ping_host_add (pingobj, hl_this->host) == 0) - { - DEBUG ("ping plugin: Successfully added host %s", hl_this->host); - /* Remove the host from the linked list */ - if (hl_prev != NULL) - hl_prev->next = hl_this->next; - else - hosts = hl_this->next; - free (hl_this->host); - free (hl_this); - hl_this = (hl_prev != NULL) ? hl_prev : hosts; - } - else - { - WARNING ("ping plugin: Failed adding host " - "`%s': %s", hl_this->host, - ping_get_error (pingobj)); - hl_this->wait_left = hl_this->wait_time; - hl_this->wait_time *= 2; - if (hl_this->wait_time > 86400) - hl_this->wait_time = 86400; - } - } - else - { - hl_this->wait_left -= interval_g; - } - - if (hl_this != NULL) - { - hl_prev = hl_this; - hl_this = hl_this->next; - } - } -} /* void add_hosts */ - -static int ping_init (void) + static pingobj_t *pingobj = NULL; + + struct timeval tv_begin; + struct timeval tv_end; + struct timespec ts_wait; + struct timespec ts_int; + + hostlist_t *hl; + int status; + + pthread_mutex_lock (&ping_lock); + + pingobj = ping_construct (); + if (pingobj == NULL) + { + ERROR ("ping plugin: ping_construct failed."); + ping_thread_error = 1; + pthread_mutex_unlock (&ping_lock); + return ((void *) -1); + } + + ping_setopt (pingobj, PING_OPT_TIMEOUT, (void *) &ping_timeout); + ping_setopt (pingobj, PING_OPT_TTL, (void *) &ping_ttl); + + /* Add all the hosts to the ping object. */ + status = 0; + for (hl = hostlist_head; hl != NULL; hl = hl->next) + { + int tmp_status; + tmp_status = ping_host_add (pingobj, hl->host); + if (tmp_status != 0) + WARNING ("ping plugin: ping_host_add (%s) failed.", hl->host); + else + status++; + } + + if (status == 0) + { + ERROR ("ping plugin: No host could be added to ping object. Giving up."); + ping_thread_error = 1; + pthread_mutex_unlock (&ping_lock); + return ((void *) -1); + } + + /* Set up `ts_int' */ + { + double temp_sec; + double temp_nsec; + + temp_nsec = modf (ping_interval, &temp_sec); + ts_int.tv_sec = (time_t) temp_sec; + ts_int.tv_nsec = (long) (temp_nsec * 1000000000L); + } + + while (ping_thread_loop > 0) + { + pingobj_iter_t *iter; + int status; + + if (gettimeofday (&tv_begin, NULL) < 0) + { + ERROR ("ping plugin: gettimeofday failed"); + ping_thread_error = 1; + break; + } + + pthread_mutex_unlock (&ping_lock); + + status = ping_send (pingobj); + if (status < 0) + { + ERROR ("ping plugin: ping_send failed: %s", ping_get_error (pingobj)); + pthread_mutex_lock (&ping_lock); + ping_thread_error = 1; + break; + } + + pthread_mutex_lock (&ping_lock); + + if (ping_thread_loop <= 0) + break; + + for (iter = ping_iterator_get (pingobj); + iter != NULL; + iter = ping_iterator_next (iter)) + { /* {{{ */ + char userhost[NI_MAXHOST]; + double latency; + size_t param_size; + + param_size = sizeof (userhost); + status = ping_iterator_get_info (iter, +#ifdef PING_INFO_USERNAME + PING_INFO_USERNAME, +#else + PING_INFO_HOSTNAME, +#endif + userhost, ¶m_size); + if (status != 0) + { + WARNING ("ping plugin: ping_iterator_get_info failed: %s", + ping_get_error (pingobj)); + continue; + } + + for (hl = hostlist_head; hl != NULL; hl = hl->next) + if (strcmp (userhost, hl->host) == 0) + break; + + if (hl == NULL) + { + WARNING ("ping plugin: Cannot find host %s.", userhost); + continue; + } + + param_size = sizeof (latency); + status = ping_iterator_get_info (iter, PING_INFO_LATENCY, + (void *) &latency, ¶m_size); + if (status != 0) + { + WARNING ("ping plugin: ping_iterator_get_info failed: %s", + ping_get_error (pingobj)); + continue; + } + + hl->pkg_sent++; + if (latency >= 0.0) + { + hl->pkg_recv++; + hl->latency_total += latency; + hl->latency_squared += (latency * latency); + } + } /* }}} for (iter) */ + + if (gettimeofday (&tv_end, NULL) < 0) + { + ERROR ("ping plugin: gettimeofday failed"); + ping_thread_error = 1; + break; + } + + /* Calculate the absolute time until which to wait and store it in + * `ts_wait'. */ + time_calc (&ts_wait, &ts_int, &tv_begin, &tv_end); + + status = pthread_cond_timedwait (&ping_cond, &ping_lock, &ts_wait); + if (ping_thread_loop <= 0) + break; + } /* while (ping_thread_loop > 0) */ + + pthread_mutex_unlock (&ping_lock); + ping_destroy (pingobj); + + return ((void *) 0); +} /* }}} void *ping_thread */ + +static int start_thread (void) /* {{{ */ { - if (pingobj == NULL) - return (-1); + int status; + + pthread_mutex_lock (&ping_lock); + + if (ping_thread_loop != 0) + { + pthread_mutex_unlock (&ping_lock); + return (-1); + } + + ping_thread_loop = 1; + ping_thread_error = 0; + status = pthread_create (&ping_thread_id, /* attr = */ NULL, + ping_thread, /* arg = */ (void *) 0); + if (status != 0) + { + ping_thread_loop = 0; + ERROR ("ping plugin: Starting thread failed."); + pthread_mutex_unlock (&ping_lock); + return (-1); + } + + pthread_mutex_unlock (&ping_lock); + return (0); +} /* }}} int start_thread */ + +static int stop_thread (void) /* {{{ */ +{ + int status; + + pthread_mutex_lock (&ping_lock); + + if (ping_thread_loop == 0) + { + pthread_mutex_unlock (&ping_lock); + return (-1); + } - if (hosts != NULL) - add_hosts (); + ping_thread_loop = 0; + pthread_cond_broadcast (&ping_cond); + pthread_mutex_unlock (&ping_lock); - return (0); -} /* int ping_init */ + status = pthread_join (ping_thread_id, /* return = */ NULL); + if (status != 0) + { + ERROR ("ping plugin: Stopping thread failed."); + status = -1; + } -static int ping_config (const char *key, const char *value) + memset (&ping_thread_id, 0, sizeof (ping_thread_id)); + ping_thread_error = 0; + + return (status); +} /* }}} int stop_thread */ + +static int ping_init (void) /* {{{ */ +{ + if (hostlist_head == NULL) + { + NOTICE ("ping plugin: No hosts have been configured."); + return (-1); + } + + if (ping_timeout > ping_interval) + { + ping_timeout = 0.9 * ping_interval; + WARNING ("ping plugin: Timeout is greater than interval. " + "Will use a timeout of %gs.", ping_timeout); + } + + if (start_thread () != 0) + return (-1); + + return (0); +} /* }}} int ping_init */ + +static int ping_config (const char *key, const char *value) /* {{{ */ { - if (pingobj == NULL) - { - if ((pingobj = ping_construct ()) == NULL) - { - ERROR ("ping plugin: `ping_construct' failed."); - return (1); - } - } - - if (strcasecmp (key, "host") == 0) - { - hostlist_t *hl; - char *host; - - if ((hl = (hostlist_t *) malloc (sizeof (hostlist_t))) == NULL) - { - char errbuf[1024]; - ERROR ("ping plugin: malloc failed: %s", - sstrerror (errno, errbuf, - sizeof (errbuf))); - return (1); - } - if ((host = strdup (value)) == NULL) - { - char errbuf[1024]; - free (hl); - ERROR ("ping plugin: strdup failed: %s", - sstrerror (errno, errbuf, - sizeof (errbuf))); - return (1); - } - - hl->host = host; - hl->wait_time = 2 * interval_g; - hl->wait_left = 0; - hl->next = hosts; - hosts = hl; - } - else if (strcasecmp (key, "ttl") == 0) - { - int ttl = atoi (value); - if (ping_setopt (pingobj, PING_OPT_TTL, (void *) &ttl)) - { - WARNING ("ping: liboping did not accept the TTL value %i", ttl); - return (1); - } - } - else - { - return (-1); - } - - return (0); -} - -static void ping_submit (char *host, double latency) + if (strcasecmp (key, "Host") == 0) + { + hostlist_t *hl; + char *host; + + hl = (hostlist_t *) malloc (sizeof (hostlist_t)); + if (hl == NULL) + { + char errbuf[1024]; + ERROR ("ping plugin: malloc failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (1); + } + + host = strdup (value); + if (host == NULL) + { + char errbuf[1024]; + sfree (hl); + ERROR ("ping plugin: strdup failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (1); + } + + hl->host = host; + hl->pkg_sent = 0; + hl->pkg_recv = 0; + hl->latency_total = 0.0; + hl->latency_squared = 0.0; + hl->next = hostlist_head; + hostlist_head = hl; + } + else if (strcasecmp (key, "TTL") == 0) + { + int ttl = atoi (value); + if ((ttl > 0) && (ttl <= 255)) + ping_ttl = ttl; + else + WARNING ("ping plugin: Ignoring invalid TTL %i.", ttl); + } + else if (strcasecmp (key, "Interval") == 0) + { + double tmp; + + tmp = atof (value); + if (tmp > 0.0) + ping_interval = tmp; + else + WARNING ("ping plugin: Ignoring invalid interval %g (%s)", + tmp, value); + } + else if (strcasecmp (key, "Timeout") == 0) + { + double tmp; + + tmp = atof (value); + if (tmp > 0.0) + ping_timeout = tmp; + else + WARNING ("ping plugin: Ignoring invalid timeout %g (%s)", + tmp, value); + } + else + { + return (-1); + } + + return (0); +} /* }}} int ping_config */ + +static void submit (const char *host, const char *type, /* {{{ */ + gauge_t value) { - value_t values[1]; - value_list_t vl = VALUE_LIST_INIT; + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; - values[0].gauge = latency; + values[0].gauge = value; - vl.values = values; - vl.values_len = 1; - sstrncpy (vl.host, hostname_g, sizeof (vl.host)); - sstrncpy (vl.plugin, "ping", sizeof (vl.plugin)); - sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance)); - sstrncpy (vl.type_instance, host, sizeof (vl.type_instance)); - sstrncpy (vl.type, "ping", sizeof (vl.type)); + vl.values = values; + vl.values_len = 1; + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "ping", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance)); + sstrncpy (vl.type_instance, host, sizeof (vl.type_instance)); + sstrncpy (vl.type, type, sizeof (vl.type)); - plugin_dispatch_values (&vl); -} + plugin_dispatch_values (&vl); +} /* }}} void ping_submit */ -static int ping_read (void) +static int ping_read (void) /* {{{ */ { - pingobj_iter_t *iter; - - char host[512]; - double latency; - size_t buf_len; - int number_of_hosts; - - if (pingobj == NULL) - return (-1); - - if (hosts != NULL) - add_hosts (); - - if (ping_send (pingobj) < 0) - { - ERROR ("ping plugin: `ping_send' failed: %s", - ping_get_error (pingobj)); - return (-1); - } - - number_of_hosts = 0; - for (iter = ping_iterator_get (pingobj); - iter != NULL; - iter = ping_iterator_next (iter)) - { - buf_len = sizeof (host); - if (ping_iterator_get_info (iter, PING_INFO_HOSTNAME, - host, &buf_len)) - { - WARNING ("ping plugin: ping_iterator_get_info " - "(PING_INFO_HOSTNAME) failed."); - continue; - } - - buf_len = sizeof (latency); - if (ping_iterator_get_info (iter, PING_INFO_LATENCY, - &latency, &buf_len)) - { - WARNING ("ping plugin: ping_iterator_get_info (%s, " - "PING_INFO_LATENCY) failed.", host); - continue; - } - - DEBUG ("ping plugin: host = %s, latency = %f", host, latency); - ping_submit (host, latency); - number_of_hosts++; - } - - if ((number_of_hosts == 0) && (getuid () != 0)) - { - ERROR ("ping plugin: All hosts failed. Try starting collectd as root."); - } - - return (number_of_hosts == 0 ? -1 : 0); -} /* int ping_read */ + hostlist_t *hl; + + if (ping_thread_error != 0) + { + ERROR ("ping plugin: The ping thread had a problem. Restarting it."); + + stop_thread (); + + for (hl = hostlist_head; hl != NULL; hl = hl->next) + { + hl->pkg_sent = 0; + hl->pkg_recv = 0; + hl->latency_total = 0.0; + hl->latency_squared = 0.0; + } + + start_thread (); + + return (-1); + } /* if (ping_thread_error != 0) */ + + for (hl = hostlist_head; hl != NULL; hl = hl->next) /* {{{ */ + { + uint32_t pkg_sent; + uint32_t pkg_recv; + double latency_total; + double latency_squared; + + double latency_average; + double latency_stddev; + + double droprate; + + /* Locking here works, because the structure of the linked list is only + * changed during configure and shutdown. */ + pthread_mutex_lock (&ping_lock); + + pkg_sent = hl->pkg_sent; + pkg_recv = hl->pkg_recv; + latency_total = hl->latency_total; + latency_squared = hl->latency_squared; + + hl->pkg_sent = 0; + hl->pkg_recv = 0; + hl->latency_total = 0.0; + hl->latency_squared = 0.0; + + pthread_mutex_unlock (&ping_lock); + + /* This e. g. happens when starting up. */ + if (pkg_sent == 0) + { + DEBUG ("ping plugin: No packages for host %s have been sent.", + hl->host); + continue; + } + + /* Calculate average. Beware of division by zero. */ + if (pkg_recv == 0) + latency_average = NAN; + else + latency_average = latency_total / ((double) pkg_recv); + + /* Calculate standard deviation. Beware even more of division by zero. */ + if (pkg_recv == 0) + latency_stddev = NAN; + else if (pkg_recv == 1) + latency_stddev = 0.0; + else + latency_stddev = sqrt (((((double) pkg_recv) * latency_squared) + - (latency_total * latency_total)) + / ((double) (pkg_recv * (pkg_recv - 1)))); + + /* Calculate drop rate. */ + droprate = ((double) (pkg_sent - pkg_recv)) / ((double) pkg_sent); + + submit (hl->host, "ping", latency_average); + submit (hl->host, "ping_stddev", latency_stddev); + submit (hl->host, "ping_droprate", droprate); + } /* }}} for (hl = hostlist_head; hl != NULL; hl = hl->next) */ + + return (0); +} /* }}} int ping_read */ + +static int ping_shutdown (void) /* {{{ */ +{ + hostlist_t *hl; + + INFO ("ping plugin: Shutting down thread."); + if (stop_thread () < 0) + return (-1); + + hl = hostlist_head; + while (hl != NULL) + { + hostlist_t *hl_next; + + hl_next = hl->next; + + sfree (hl->host); + sfree (hl); + + hl = hl_next; + } + + return (0); +} /* }}} int ping_shutdown */ void module_register (void) { - plugin_register_config ("ping", ping_config, - config_keys, config_keys_num); - plugin_register_init ("ping", ping_init); - plugin_register_read ("ping", ping_read); + plugin_register_config ("ping", ping_config, + config_keys, config_keys_num); + plugin_register_init ("ping", ping_init); + plugin_register_read ("ping", ping_read); + plugin_register_shutdown ("ping", ping_shutdown); } /* void module_register */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/plugin.c b/src/plugin.c index 74565c35..b120e7ba 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -34,6 +34,7 @@ #include "configfile.h" #include "utils_avltree.h" #include "utils_llist.h" +#include "utils_heap.h" #include "utils_cache.h" #include "utils_threshold.h" #include "filter_chain.h" @@ -41,12 +42,27 @@ /* * Private structures */ +struct callback_func_s +{ + void *cf_callback; + user_data_t cf_udata; +}; +typedef struct callback_func_s callback_func_t; + +#define RF_SIMPLE 0 +#define RF_COMPLEX 1 struct read_func_s { - int wait_time; - int wait_left; - int (*callback) (void); - enum { DONE = 0, TODO = 1, ACTIVE = 2 } needs_read; + /* `read_func_t' "inherits" from `callback_func_t'. + * The `rf_super' member MUST be the first one in this structure! */ +#define rf_callback rf_super.cf_callback +#define rf_udata rf_super.cf_udata + callback_func_t rf_super; + char rf_name[DATA_MAX_NAME_LEN]; + int rf_type; + struct timespec rf_interval; + struct timespec rf_effective_interval; + struct timespec rf_next_read; }; typedef struct read_func_s read_func_t; @@ -54,7 +70,6 @@ typedef struct read_func_s read_func_t; * Private variables */ static llist_t *list_init; -static llist_t *list_read; static llist_t *list_write; static llist_t *list_flush; static llist_t *list_shutdown; @@ -68,6 +83,7 @@ static c_avl_tree_t *data_sets; static char *plugindir = NULL; +static c_heap_t *read_heap = NULL; static int read_loop = 1; static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t read_cond = PTHREAD_COND_INITIALIZER; @@ -85,26 +101,101 @@ static const char *plugin_get_dir (void) return (plugindir); } -static int register_callback (llist_t **list, const char *name, void *callback) +static void destroy_callback (callback_func_t *cf) /* {{{ */ +{ + if (cf == NULL) + return; + + if ((cf->cf_udata.data != NULL) && (cf->cf_udata.free_func != NULL)) + { + cf->cf_udata.free_func (cf->cf_udata.data); + cf->cf_udata.data = NULL; + cf->cf_udata.free_func = NULL; + } + sfree (cf); +} /* }}} void destroy_callback */ + +static void destroy_all_callbacks (llist_t **list) /* {{{ */ +{ + llentry_t *le; + + if (*list == NULL) + return; + + le = llist_head (*list); + while (le != NULL) + { + llentry_t *le_next; + + le_next = le->next; + + sfree (le->key); + destroy_callback (le->value); + le->value = NULL; + + le = le_next; + } + + llist_destroy (*list); + *list = NULL; +} /* }}} void destroy_all_callbacks */ + +static void destroy_read_heap (void) /* {{{ */ +{ + if (read_heap == NULL) + return; + + while (42) + { + callback_func_t *cf; + + cf = c_head_get_root (read_heap); + if (cf == NULL) + break; + + destroy_callback (cf); + } + + c_heap_destroy (read_heap); + read_heap = NULL; +} /* }}} void destroy_read_heap */ + +static int register_callback (llist_t **list, /* {{{ */ + const char *name, callback_func_t *cf) { llentry_t *le; char *key; - if ((*list == NULL) - && ((*list = llist_create ()) == NULL)) + if (*list == NULL) + { + *list = llist_create (); + if (*list == NULL) + { + ERROR ("plugin: create_register_callback: " + "llist_create failed."); + destroy_callback (cf); + return (-1); + } + } + + key = strdup (name); + if (key == NULL) + { + ERROR ("plugin: create_register_callback: strdup failed."); + destroy_callback (cf); return (-1); + } le = llist_search (*list, name); if (le == NULL) { - key = strdup (name); - if (key == NULL) - return (-1); - - le = llentry_create (key, callback); + le = llentry_create (key, cf); if (le == NULL) { + ERROR ("plugin: create_register_callback: " + "llentry_create failed."); free (key); + destroy_callback (cf); return (-1); } @@ -112,27 +203,65 @@ static int register_callback (llist_t **list, const char *name, void *callback) } else { - le->value = callback; + callback_func_t *old_cf; + + old_cf = le->value; + le->value = cf; + + destroy_callback (old_cf); + sfree (key); } return (0); -} /* int register_callback */ +} /* }}} int register_callback */ -static int plugin_unregister (llist_t *list, const char *name) +static int create_register_callback (llist_t **list, /* {{{ */ + const char *name, void *callback, user_data_t *ud) +{ + callback_func_t *cf; + + cf = (callback_func_t *) malloc (sizeof (*cf)); + if (cf == NULL) + { + ERROR ("plugin: create_register_callback: malloc failed."); + return (-1); + } + memset (cf, 0, sizeof (*cf)); + + cf->cf_callback = callback; + if (ud == NULL) + { + cf->cf_udata.data = NULL; + cf->cf_udata.free_func = NULL; + } + else + { + cf->cf_udata = *ud; + } + + return (register_callback (list, name, cf)); +} /* }}} int create_register_callback */ + +static int plugin_unregister (llist_t *list, const char *name) /* {{{ */ { llentry_t *e; - e = llist_search (list, name); + if (list == NULL) + return (-1); + e = llist_search (list, name); if (e == NULL) return (-1); llist_remove (list, e); - free (e->key); + + sfree (e->key); + destroy_callback (e->value); + llentry_destroy (e); return (0); -} /* int plugin_unregister */ +} /* }}} int plugin_unregister */ /* * (Try to) load the shared object `file'. Won't complain if it isn't a shared @@ -173,78 +302,144 @@ static int plugin_load_file (char *file) static void *plugin_read_thread (void __attribute__((unused)) *args) { - llentry_t *le; - read_func_t *rf; - int status; - int done; - - pthread_mutex_lock (&read_lock); - while (read_loop != 0) { - le = llist_head (list_read); - done = 0; + read_func_t *rf; + struct timeval now; + int status; - while ((read_loop != 0) && (le != NULL)) + /* Get the read function that needs to be read next. */ + rf = c_head_get_root (read_heap); + if (rf == NULL) { - rf = (read_func_t *) le->value; + struct timespec abstime; - if (rf->needs_read != TODO) - { - le = le->next; - continue; - } + gettimeofday (&now, /* timezone = */ NULL); - /* We will do this read function */ - rf->needs_read = ACTIVE; + abstime.tv_sec = now.tv_sec + interval_g; + abstime.tv_nsec = 1000 * now.tv_usec; - DEBUG ("[thread #%5lu] plugin: plugin_read_thread: Handling %s", - (unsigned long int) pthread_self (), le->key); + pthread_mutex_lock (&read_lock); + pthread_cond_timedwait (&read_cond, &read_lock, + &abstime); pthread_mutex_unlock (&read_lock); + continue; + } - status = rf->callback (); - done++; + if ((rf->rf_interval.tv_sec == 0) && (rf->rf_interval.tv_nsec == 0)) + { + gettimeofday (&now, /* timezone = */ NULL); - if (status != 0) - { - if (rf->wait_time < interval_g) - rf->wait_time = interval_g; - rf->wait_left = rf->wait_time; - rf->wait_time = rf->wait_time * 2; - if (rf->wait_time > 86400) - rf->wait_time = 86400; - - NOTICE ("read-function of plugin `%s' " - "failed. Will suspend it for %i " - "seconds.", le->key, rf->wait_left); - } - else + rf->rf_interval.tv_sec = interval_g; + rf->rf_interval.tv_nsec = 0; + + rf->rf_effective_interval = rf->rf_interval; + + rf->rf_next_read.tv_sec = now.tv_sec; + rf->rf_next_read.tv_nsec = 1000 * now.tv_usec; + } + + /* sleep until this entry is due, + * using pthread_cond_timedwait */ + pthread_mutex_lock (&read_lock); + pthread_cond_timedwait (&read_cond, &read_lock, + &rf->rf_next_read); + pthread_mutex_unlock (&read_lock); + + /* Check if we're supposed to stop.. This may have interrupted + * the sleep, too. */ + if (read_loop == 0) + { + /* Insert `rf' again, so it can be free'd correctly */ + c_heap_insert (read_heap, rf); + break; + } + + DEBUG ("plugin_read_thread: Handling `%s'.", rf->rf_name); + + if (rf->rf_type == RF_SIMPLE) + { + int (*callback) (void); + + callback = rf->rf_callback; + status = (*callback) (); + } + else + { + plugin_read_cb callback; + + callback = rf->rf_callback; + status = (*callback) (&rf->rf_udata); + } + + /* If the function signals failure, we will increase the + * intervals in which it will be called. */ + if (status != 0) + { + rf->rf_effective_interval.tv_sec *= 2; + rf->rf_effective_interval.tv_nsec *= 2; + NORMALIZE_TIMESPEC (rf->rf_effective_interval); + + if (rf->rf_effective_interval.tv_sec >= 86400) { - rf->wait_left = 0; - rf->wait_time = interval_g; + rf->rf_effective_interval.tv_sec = 86400; + rf->rf_effective_interval.tv_nsec = 0; } - pthread_mutex_lock (&read_lock); - - rf->needs_read = DONE; - le = le->next; - } /* while (le != NULL) */ + NOTICE ("read-function of plugin `%s' failed. " + "Will suspend it for %i seconds.", + rf->rf_name, + (int) rf->rf_effective_interval.tv_sec); + } + else + { + /* Success: Restore the interval, if it was changed. */ + rf->rf_effective_interval = rf->rf_interval; + } - if ((read_loop != 0) && (done == 0)) + /* update the ``next read due'' field */ + gettimeofday (&now, /* timezone = */ NULL); + + DEBUG ("plugin_read_thread: Effective interval of the " + "%s plugin is %i.%09i.", + rf->rf_name, + (int) rf->rf_effective_interval.tv_sec, + (int) rf->rf_effective_interval.tv_nsec); + + /* Calculate the next (absolute) time at which this function + * should be called. */ + rf->rf_next_read.tv_sec = rf->rf_next_read.tv_sec + + rf->rf_effective_interval.tv_sec; + rf->rf_next_read.tv_nsec = rf->rf_next_read.tv_nsec + + rf->rf_effective_interval.tv_nsec; + NORMALIZE_TIMESPEC (rf->rf_next_read); + + /* Check, if `rf_next_read' is in the past. */ + if ((rf->rf_next_read.tv_sec < now.tv_sec) + || ((rf->rf_next_read.tv_sec == now.tv_sec) + && (rf->rf_next_read.tv_nsec < (1000 * now.tv_usec)))) { - DEBUG ("[thread #%5lu] plugin: plugin_read_thread: Waiting on read_cond.", - (unsigned long int) pthread_self ()); - pthread_cond_wait (&read_cond, &read_lock); + /* `rf_next_read' is in the past. Insert `now' + * so this value doesn't trail off into the + * past too much. */ + rf->rf_next_read.tv_sec = now.tv_sec; + rf->rf_next_read.tv_nsec = 1000 * now.tv_usec; } - } /* while (read_loop) */ - pthread_mutex_unlock (&read_lock); + DEBUG ("plugin_read_thread: Next read of the %s plugin at %i.%09i.", + rf->rf_name, + (int) rf->rf_next_read.tv_sec, + (int) rf->rf_next_read.tv_nsec); + + /* Re-insert this read function into the heap again. */ + c_heap_insert (read_heap, rf); + } /* while (read_loop) */ pthread_exit (NULL); return ((void *) 0); } /* void *plugin_read_thread */ -static void start_threads (int num) +static void start_read_threads (int num) { int i; @@ -254,7 +449,7 @@ static void start_threads (int num) read_threads = (pthread_t *) calloc (num, sizeof (pthread_t)); if (read_threads == NULL) { - ERROR ("plugin: start_threads: calloc failed."); + ERROR ("plugin: start_read_threads: calloc failed."); return; } @@ -268,22 +463,24 @@ static void start_threads (int num) } else { - ERROR ("plugin: start_threads: pthread_create failed."); + ERROR ("plugin: start_read_threads: pthread_create failed."); return; } } /* for (i) */ -} /* void start_threads */ +} /* void start_read_threads */ -static void stop_threads (void) +static void stop_read_threads (void) { int i; if (read_threads == NULL) return; + INFO ("collectd: Stopping %i read threads.", read_threads_num); + pthread_mutex_lock (&read_lock); read_loop = 0; - DEBUG ("plugin: stop_threads: Signalling `read_cond'"); + DEBUG ("plugin: stop_read_threads: Signalling `read_cond'"); pthread_cond_broadcast (&read_cond); pthread_mutex_unlock (&read_lock); @@ -291,13 +488,13 @@ static void stop_threads (void) { if (pthread_join (read_threads[i], NULL) != 0) { - ERROR ("plugin: stop_threads: pthread_join failed."); + ERROR ("plugin: stop_read_threads: pthread_join failed."); } read_threads[i] = (pthread_t) 0; } sfree (read_threads); read_threads_num = 0; -} /* void stop_threads */ +} /* void stop_read_threads */ /* * Public functions @@ -420,14 +617,46 @@ int plugin_register_complex_config (const char *type, int plugin_register_init (const char *name, int (*callback) (void)) { - return (register_callback (&list_init, name, (void *) callback)); + return (create_register_callback (&list_init, name, (void *) callback, + /* user_data = */ NULL)); } /* plugin_register_init */ +static int plugin_compare_read_func (const void *arg0, const void *arg1) +{ + const read_func_t *rf0; + const read_func_t *rf1; + + rf0 = arg0; + rf1 = arg1; + + if (rf0->rf_next_read.tv_sec < rf1->rf_next_read.tv_sec) + return (-1); + else if (rf0->rf_next_read.tv_sec > rf1->rf_next_read.tv_sec) + return (1); + else if (rf0->rf_next_read.tv_nsec < rf1->rf_next_read.tv_nsec) + return (-1); + else if (rf0->rf_next_read.tv_nsec > rf1->rf_next_read.tv_nsec) + return (1); + else + return (0); +} /* int plugin_compare_read_func */ + int plugin_register_read (const char *name, int (*callback) (void)) { read_func_t *rf; + if (read_heap == NULL) + { + read_heap = c_heap_create (plugin_compare_read_func); + if (read_heap == NULL) + { + ERROR ("plugin_register_read: " + "c_heap_create failed."); + return (-1); + } + } + rf = (read_func_t *) malloc (sizeof (read_func_t)); if (rf == NULL) { @@ -437,31 +666,86 @@ int plugin_register_read (const char *name, return (-1); } - memset (rf, '\0', sizeof (read_func_t)); - rf->wait_time = interval_g; - rf->wait_left = 0; - rf->callback = callback; - rf->needs_read = DONE; - - return (register_callback (&list_read, name, (void *) rf)); + memset (rf, 0, sizeof (read_func_t)); + rf->rf_callback = (void *) callback; + rf->rf_udata.data = NULL; + rf->rf_udata.free_func = NULL; + sstrncpy (rf->rf_name, name, sizeof (rf->rf_name)); + rf->rf_type = RF_SIMPLE; + rf->rf_interval.tv_sec = 0; + rf->rf_interval.tv_nsec = 0; + rf->rf_effective_interval = rf->rf_interval; + + return (c_heap_insert (read_heap, rf)); } /* int plugin_register_read */ +int plugin_register_complex_read (const char *name, + plugin_read_cb callback, + const struct timespec *interval, + user_data_t *user_data) +{ + read_func_t *rf; + + if (read_heap == NULL) + { + read_heap = c_heap_create (plugin_compare_read_func); + if (read_heap == NULL) + { + ERROR ("plugin_register_read: c_heap_create failed."); + return (-1); + } + } + + rf = (read_func_t *) malloc (sizeof (read_func_t)); + if (rf == NULL) + { + ERROR ("plugin_register_complex_read: malloc failed."); + return (-1); + } + + memset (rf, 0, sizeof (read_func_t)); + rf->rf_callback = (void *) callback; + sstrncpy (rf->rf_name, name, sizeof (rf->rf_name)); + rf->rf_type = RF_COMPLEX; + if (interval != NULL) + { + rf->rf_interval = *interval; + } + rf->rf_effective_interval = rf->rf_interval; + + /* Set user data */ + if (user_data == NULL) + { + rf->rf_udata.data = NULL; + rf->rf_udata.free_func = NULL; + } + else + { + rf->rf_udata = *user_data; + } + + return (c_heap_insert (read_heap, rf)); +} /* int plugin_register_complex_read */ + int plugin_register_write (const char *name, - int (*callback) (const data_set_t *ds, const value_list_t *vl)) + plugin_write_cb callback, user_data_t *ud) { - return (register_callback (&list_write, name, (void *) callback)); + return (create_register_callback (&list_write, name, + (void *) callback, ud)); } /* int plugin_register_write */ int plugin_register_flush (const char *name, - int (*callback) (const int timeout, const char *identifier)) + plugin_flush_cb callback, user_data_t *ud) { - return (register_callback (&list_flush, name, (void *) callback)); + return (create_register_callback (&list_flush, name, + (void *) callback, ud)); } /* int plugin_register_flush */ int plugin_register_shutdown (char *name, int (*callback) (void)) { - return (register_callback (&list_shutdown, name, (void *) callback)); + return (create_register_callback (&list_shutdown, name, + (void *) callback, /* user_data = */ NULL)); } /* int plugin_register_shutdown */ int plugin_register_data_set (const data_set_t *ds) @@ -501,16 +785,18 @@ int plugin_register_data_set (const data_set_t *ds) return (c_avl_insert (data_sets, (void *) ds_copy->type, (void *) ds_copy)); } /* int plugin_register_data_set */ -int plugin_register_log (char *name, - void (*callback) (int priority, const char *msg)) +int plugin_register_log (const char *name, + plugin_log_cb callback, user_data_t *ud) { - return (register_callback (&list_log, name, (void *) callback)); + return (create_register_callback (&list_log, name, + (void *) callback, ud)); } /* int plugin_register_log */ int plugin_register_notification (const char *name, - int (*callback) (const notification_t *notif)) + plugin_notification_cb callback, user_data_t *ud) { - return (register_callback (&list_notification, name, (void *) callback)); + return (create_register_callback (&list_notification, name, + (void *) callback, ud)); } /* int plugin_register_log */ int plugin_unregister_config (const char *name) @@ -532,19 +818,9 @@ int plugin_unregister_init (const char *name) int plugin_unregister_read (const char *name) { - llentry_t *e; - - e = llist_search (list_read, name); - - if (e == NULL) - return (-1); - - llist_remove (list_read, e); - free (e->value); - free (e->key); - llentry_destroy (e); - - return (0); + /* TODO: Implement removal of a specific key from the heap. */ + assert (0); + return (-1); } int plugin_unregister_write (const char *name) @@ -591,7 +867,6 @@ int plugin_unregister_notification (const char *name) void plugin_init_all (void) { const char *chain_name; - int (*callback) (void); llentry_t *le; int status; @@ -605,7 +880,7 @@ void plugin_init_all (void) post_cache_chain = fc_chain_get_by_name (chain_name); - if ((list_init == NULL) && (list_read == NULL)) + if ((list_init == NULL) && (read_heap == NULL)) return; /* Calling all init callbacks before checking if read callbacks @@ -614,7 +889,11 @@ void plugin_init_all (void) le = llist_head (list_init); while (le != NULL) { - callback = (int (*) (void)) le->value; + callback_func_t *cf; + plugin_init_cb callback; + + cf = le->value; + callback = cf->cf_callback; status = (*callback) (); if (status != 0) @@ -634,82 +913,68 @@ void plugin_init_all (void) } /* Start read-threads */ - if (list_read != NULL) + if (read_heap != NULL) { const char *rt; int num; rt = global_option_get ("ReadThreads"); num = atoi (rt); if (num != -1) - start_threads ((num > 0) ? num : 5); + start_read_threads ((num > 0) ? num : 5); } } /* void plugin_init_all */ +/* TODO: Rename this function. */ void plugin_read_all (void) { - llentry_t *le; - read_func_t *rf; - uc_check_timeout (); - if (list_read == NULL) - return; - - pthread_mutex_lock (&read_lock); - - le = llist_head (list_read); - while (le != NULL) - { - rf = (read_func_t *) le->value; - - if (rf->needs_read != DONE) - { - le = le->next; - continue; - } - - if (rf->wait_left > 0) - rf->wait_left -= interval_g; - - if (rf->wait_left <= 0) - { - rf->needs_read = TODO; - } - - le = le->next; - } - - DEBUG ("plugin: plugin_read_all: Signalling `read_cond'"); - pthread_cond_broadcast (&read_cond); - pthread_mutex_unlock (&read_lock); + return; } /* void plugin_read_all */ /* Read function called when the `-T' command line argument is given. */ int plugin_read_all_once (void) { - llentry_t *le; - read_func_t *rf; int status; int return_status = 0; - if (list_read == NULL) + if (read_heap == NULL) { NOTICE ("No read-functions are registered."); return (0); } - for (le = llist_head (list_read); - le != NULL; - le = le->next) + while (42) { - rf = (read_func_t *) le->value; - status = rf->callback (); + read_func_t *rf; + + rf = c_head_get_root (read_heap); + if (rf == NULL) + break; + + if (rf->rf_type == RF_SIMPLE) + { + int (*callback) (void); + + callback = rf->rf_callback; + status = (*callback) (); + } + else + { + plugin_read_cb callback; + + callback = rf->rf_callback; + status = (*callback) (&rf->rf_udata); + } + if (status != 0) { NOTICE ("read-function of plugin `%s' failed.", - le->key); + rf->rf_name); return_status = -1; } + + destroy_callback ((void *) rf); } return (return_status); @@ -718,7 +983,6 @@ int plugin_read_all_once (void) int plugin_write (const char *plugin, /* {{{ */ const data_set_t *ds, const value_list_t *vl) { - int (*callback) (const data_set_t *ds, const value_list_t *vl); llentry_t *le; int status; @@ -746,8 +1010,12 @@ int plugin_write (const char *plugin, /* {{{ */ le = llist_head (list_write); while (le != NULL) { - callback = le->value; - status = (*callback) (ds, vl); + callback_func_t *cf = le->value; + plugin_write_cb callback; + + DEBUG ("plugin: plugin_write: Writing values via %s.", le->key); + callback = cf->cf_callback; + status = (*callback) (ds, vl, &cf->cf_udata); if (status != 0) failure++; else @@ -763,6 +1031,9 @@ int plugin_write (const char *plugin, /* {{{ */ } else /* plugin != NULL */ { + callback_func_t *cf; + plugin_write_cb callback; + le = llist_head (list_write); while (le != NULL) { @@ -775,8 +1046,11 @@ int plugin_write (const char *plugin, /* {{{ */ if (le == NULL) return (ENOENT); - callback = le->value; - status = (*callback) (ds, vl); + cf = le->value; + + DEBUG ("plugin: plugin_write: Writing values via %s.", le->key); + callback = cf->cf_callback; + status = (*callback) (ds, vl, &cf->cf_udata); } return (status); @@ -784,7 +1058,6 @@ int plugin_write (const char *plugin, /* {{{ */ int plugin_flush (const char *plugin, int timeout, const char *identifier) { - int (*callback) (int timeout, const char *identifier); llentry_t *le; if (list_flush == NULL) @@ -793,6 +1066,9 @@ int plugin_flush (const char *plugin, int timeout, const char *identifier) le = llist_head (list_flush); while (le != NULL) { + callback_func_t *cf; + plugin_flush_cb callback; + if ((plugin != NULL) && (strcmp (plugin, le->key) != 0)) { @@ -800,8 +1076,10 @@ int plugin_flush (const char *plugin, int timeout, const char *identifier) continue; } - callback = (int (*) (int, const char *)) le->value; - (*callback) (timeout, identifier); + cf = le->value; + callback = cf->cf_callback; + + (*callback) (timeout, identifier, &cf->cf_udata); le = le->next; } @@ -810,18 +1088,27 @@ int plugin_flush (const char *plugin, int timeout, const char *identifier) void plugin_shutdown_all (void) { - int (*callback) (void); llentry_t *le; - stop_threads (); + stop_read_threads (); - if (list_shutdown == NULL) - return; + destroy_all_callbacks (&list_init); + destroy_read_heap (); + + plugin_flush (/* plugin = */ NULL, /* timeout = */ -1, + /* identifier = */ NULL); + + le = NULL; + if (list_shutdown != NULL) + le = llist_head (list_shutdown); - le = llist_head (list_shutdown); while (le != NULL) { - callback = (int (*) (void)) le->value; + callback_func_t *cf; + plugin_shutdown_cb callback; + + cf = le->value; + callback = cf->cf_callback; /* Advance the pointer before calling the callback allows * shutdown functions to unregister themselves. If done the @@ -831,6 +1118,12 @@ void plugin_shutdown_all (void) (*callback) (); } + + destroy_all_callbacks (&list_write); + destroy_all_callbacks (&list_flush); + destroy_all_callbacks (&list_notification); + destroy_all_callbacks (&list_shutdown); + destroy_all_callbacks (&list_log); } /* void plugin_shutdown_all */ int plugin_dispatch_values (value_list_t *vl) @@ -873,6 +1166,9 @@ int plugin_dispatch_values (value_list_t *vl) if (vl->time == 0) vl->time = time (NULL); + if (vl->interval <= 0) + vl->interval = interval_g; + DEBUG ("plugin_dispatch_values: time = %u; interval = %i; " "host = %s; " "plugin = %s; plugin_instance = %s; " @@ -989,7 +1285,6 @@ int plugin_dispatch_values (value_list_t *vl) int plugin_dispatch_notification (const notification_t *notif) { - int (*callback) (const notification_t *); llentry_t *le; /* Possible TODO: Add flap detection here */ @@ -1005,8 +1300,19 @@ int plugin_dispatch_notification (const notification_t *notif) le = llist_head (list_notification); while (le != NULL) { - callback = (int (*) (const notification_t *)) le->value; - (*callback) (notif); + callback_func_t *cf; + plugin_notification_cb callback; + int status; + + cf = le->value; + callback = cf->cf_callback; + status = (*callback) (notif, &cf->cf_udata); + if (status != 0) + { + WARNING ("plugin_dispatch_notification: Notification " + "callback %s returned %i.", + le->key, status); + } le = le->next; } @@ -1018,8 +1324,6 @@ void plugin_log (int level, const char *format, ...) { char msg[1024]; va_list ap; - - void (*callback) (int, const char *); llentry_t *le; if (list_log == NULL) @@ -1038,8 +1342,13 @@ void plugin_log (int level, const char *format, ...) le = llist_head (list_log); while (le != NULL) { - callback = (void (*) (int, const char *)) le->value; - (*callback) (level, msg); + callback_func_t *cf; + plugin_log_cb callback; + + cf = le->value; + callback = cf->cf_callback; + + (*callback) (level, msg, &cf->cf_udata); le = le->next; } @@ -1232,3 +1541,5 @@ int plugin_notification_meta_free (notification_meta_t *n) return (0); } /* int plugin_notification_meta_free */ + +/* vim: set sw=8 ts=8 noet fdm=marker : */ diff --git a/src/plugin.h b/src/plugin.h index 3088e06e..e54b27d6 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -136,6 +136,28 @@ typedef struct notification_s notification_meta_t *meta; } notification_t; +struct user_data_s +{ + void *data; + void (*free_func) (void *); +}; +typedef struct user_data_s user_data_t; + +/* + * Callback types + */ +typedef int (*plugin_init_cb) (void); +typedef int (*plugin_read_cb) (user_data_t *); +typedef int (*plugin_write_cb) (const data_set_t *, const value_list_t *, + user_data_t *); +typedef int (*plugin_flush_cb) (int timeout, const char *identifier, + user_data_t *); +typedef void (*plugin_log_cb) (int severity, const char *message, + user_data_t *); +typedef int (*plugin_shutdown_cb) (void); +typedef int (*plugin_notification_cb) (const notification_t *, + user_data_t *); + /* * NAME * plugin_set_dir @@ -222,25 +244,30 @@ int plugin_register_config (const char *name, int plugin_register_complex_config (const char *type, int (*callback) (oconfig_item_t *)); int plugin_register_init (const char *name, - int (*callback) (void)); + plugin_init_cb callback); int plugin_register_read (const char *name, int (*callback) (void)); +int plugin_register_complex_read (const char *name, + plugin_read_cb callback, + const struct timespec *interval, + user_data_t *user_data); int plugin_register_write (const char *name, - int (*callback) (const data_set_t *ds, const value_list_t *vl)); + plugin_write_cb callback, user_data_t *user_data); int plugin_register_flush (const char *name, - int (*callback) (const int timeout, const char *identifier)); + plugin_flush_cb callback, user_data_t *user_data); int plugin_register_shutdown (char *name, - int (*callback) (void)); + plugin_shutdown_cb callback); int plugin_register_data_set (const data_set_t *ds); -int plugin_register_log (char *name, - void (*callback) (int, const char *)); +int plugin_register_log (const char *name, + plugin_log_cb callback, user_data_t *user_data); int plugin_register_notification (const char *name, - int (*callback) (const notification_t *notif)); + plugin_notification_cb callback, user_data_t *user_data); int plugin_unregister_config (const char *name); int plugin_unregister_complex_config (const char *name); int plugin_unregister_init (const char *name); int plugin_unregister_read (const char *name); +int plugin_unregister_complex_read (const char *name, void **user_data); int plugin_unregister_write (const char *name); int plugin_unregister_flush (const char *name); int plugin_unregister_shutdown (const char *name); diff --git a/src/powerdns.c b/src/powerdns.c index 164137ba..beb49fbb 100644 --- a/src/powerdns.c +++ b/src/powerdns.c @@ -289,30 +289,11 @@ static void submit (const char *plugin_instance, /* {{{ */ return; } - if (ds->ds[0].type == DS_TYPE_GAUGE) + if (0 != parse_value (value, &values[0], ds->ds[0])) { - char *endptr = NULL; - - values[0].gauge = strtod (value, &endptr); - - if (endptr == value) - { - ERROR ("powerdns plugin: Cannot convert `%s' " - "to a floating point number.", value); - return; - } - } - else - { - char *endptr = NULL; - - values[0].counter = strtoll (value, &endptr, 0); - if (endptr == value) - { - ERROR ("powerdns plugin: Cannot convert `%s' " - "to an integer number.", value); - return; - } + ERROR ("powerdns plugin: Cannot convert `%s' " + "to a number.", value); + return; } vl.values = values; diff --git a/src/protocols.c b/src/protocols.c new file mode 100644 index 00000000..75e9a1c4 --- /dev/null +++ b/src/protocols.c @@ -0,0 +1,248 @@ +/** + * collectd - src/protocols.c + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "utils_ignorelist.h" + +#if !KERNEL_LINUX +# error "No applicable input method." +#endif + +#define SNMP_FILE "/proc/net/snmp" +#define NETSTAT_FILE "/proc/net/netstat" + +/* + * Global variables + */ +static const char *config_keys[] = +{ + "Value", + "IgnoreSelected", +}; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +static ignorelist_t *values_list = NULL; + +/* + * Functions + */ +static void submit (const char *protocol_name, + const char *str_key, const char *str_value) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + char *tmp_ptr; + + errno = 0; + tmp_ptr = NULL; + values[0].counter = (counter_t) strtoll (str_value, &tmp_ptr, + /* base = */ 0); + if ((errno != 0) || (tmp_ptr == str_value)) + { + ERROR ("protocols plugin: Parsing string as integer failed: %s", + str_value); + return; + } + + vl.values = values; + vl.values_len = 1; + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "protocols", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, protocol_name, sizeof (vl.plugin_instance)); + sstrncpy (vl.type, "protocol_counter", sizeof (vl.type)); + sstrncpy (vl.type_instance, str_key, sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} /* void submit */ + +static int read_file (const char *path) +{ + FILE *fh; + char key_buffer[4096]; + char value_buffer[4096]; + char *key_ptr; + char *value_ptr; + char *key_fields[256]; + char *value_fields[256]; + int key_fields_num; + int value_fields_num; + int status; + int i; + + fh = fopen (path, "r"); + if (fh == NULL) + { + ERROR ("protocols plugin: fopen (%s) failed: %s.", + path, sstrerror (errno, key_buffer, sizeof (key_buffer))); + return (-1); + } + + status = -1; + while (42) + { + clearerr (fh); + key_ptr = fgets (key_buffer, sizeof (key_buffer), fh); + if (key_ptr == NULL) + { + if (feof (fh) != 0) + { + status = 0; + break; + } + else if (ferror (fh) != 0) + { + ERROR ("protocols plugin: Reading from %s failed.", path); + break; + } + else + { + ERROR ("protocols plugin: fgets failed for an unknown reason."); + break; + } + } /* if (key_ptr == NULL) */ + + value_ptr = fgets (value_buffer, sizeof (value_buffer), fh); + if (value_ptr == NULL) + { + ERROR ("protocols plugin: read_file (%s): Could not read values line.", + path); + break; + } + + key_ptr = strchr (key_buffer, ':'); + if (key_ptr == NULL) + { + ERROR ("protocols plugin: Could not find protocol name in keys line."); + break; + } + *key_ptr = 0; + key_ptr++; + + value_ptr = strchr (value_buffer, ':'); + if (value_ptr == NULL) + { + ERROR ("protocols plugin: Could not find protocol name " + "in values line."); + break; + } + *value_ptr = 0; + value_ptr++; + + if (strcmp (key_buffer, value_buffer) != 0) + { + ERROR ("protocols plugin: Protocol names in keys and values lines " + "don't match: `%s' vs. `%s'.", + key_buffer, value_buffer); + break; + } + + + key_fields_num = strsplit (key_ptr, + key_fields, STATIC_ARRAY_SIZE (key_fields)); + value_fields_num = strsplit (value_ptr, + value_fields, STATIC_ARRAY_SIZE (value_fields)); + + if (key_fields_num != value_fields_num) + { + ERROR ("protocols plugin: Number of fields in keys and values lines " + "dont match: %i vs %i.", + key_fields_num, value_fields_num); + break; + } + + for (i = 0; i < key_fields_num; i++) + { + if (values_list != NULL) + { + char match_name[2 * DATA_MAX_NAME_LEN]; + + ssnprintf (match_name, sizeof (match_name), "%s:%s", + key_buffer, key_fields[i]); + + if (ignorelist_match (values_list, match_name)) + continue; + } /* if (values_list != NULL) */ + + submit (key_buffer, key_fields[i], value_fields[i]); + } /* for (i = 0; i < key_fields_num; i++) */ + } /* while (42) */ + + fclose (fh); + + return (status); +} /* int read_file */ + +static int protocols_read (void) +{ + int status; + int success = 0; + + status = read_file (SNMP_FILE); + if (status == 0) + success++; + + status = read_file (NETSTAT_FILE); + if (status == 0) + success++; + + if (success == 0) + return (-1); + + return (0); +} /* int protocols_read */ + +static int protocols_config (const char *key, const char *value) +{ + if (values_list == NULL) + values_list = ignorelist_create (/* invert = */ 1); + + if (strcasecmp (key, "Value") == 0) + { + ignorelist_add (values_list, value); + } + else if (strcasecmp (key, "IgnoreSelected") == 0) + { + int invert = 1; + if ((strcasecmp (value, "True") == 0) + || (strcasecmp (value, "Yes") == 0) + || (strcasecmp (value, "On") == 0)) + invert = 0; + ignorelist_set_invert (values_list, invert); + } + else + { + return (-1); + } + + return (0); +} /* int protocols_config */ + +void module_register (void) +{ + plugin_register_config ("protocols", protocols_config, + config_keys, config_keys_num); + plugin_register_read ("protocols", protocols_read); +} /* void module_register */ + +/* vim: set sw=2 sts=2 et : */ diff --git a/src/rrdcached.c b/src/rrdcached.c index 31c63524..326a8898 100644 --- a/src/rrdcached.c +++ b/src/rrdcached.c @@ -314,7 +314,8 @@ static int rc_init (void) return (0); } /* int rc_init */ -static int rc_write (const data_set_t *ds, const value_list_t *vl) +static int rc_write (const data_set_t *ds, const value_list_t *vl, + user_data_t __attribute__((unused)) *user_data) { char filename[512]; char values[512]; @@ -405,7 +406,7 @@ void module_register (void) plugin_register_config ("rrdcached", rc_config, config_keys, config_keys_num); plugin_register_init ("rrdcached", rc_init); - plugin_register_write ("rrdcached", rc_write); + plugin_register_write ("rrdcached", rc_write, /* user_data = */ NULL); plugin_register_shutdown ("rrdcached", rc_shutdown); } /* void module_register */ diff --git a/src/rrdtool.c b/src/rrdtool.c index b80e1109..19f351ab 100644 --- a/src/rrdtool.c +++ b/src/rrdtool.c @@ -111,7 +111,8 @@ static rrd_queue_t *queue_head = NULL; static rrd_queue_t *queue_tail = NULL; static rrd_queue_t *flushq_head = NULL; static rrd_queue_t *flushq_tail = NULL; -static pthread_t queue_thread = 0; +static pthread_t queue_thread; +static int queue_thread_running = 1; static pthread_mutex_t queue_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER; @@ -400,8 +401,9 @@ static void *rrd_queue_thread (void __attribute__((unused)) *data) /* Write the values to the RRD-file */ srrd_update (queue_entry->filename, NULL, values_num, (const char **)values); - DEBUG ("rrdtool plugin: queue thread: Wrote %i values to %s", - values_num, queue_entry->filename); + DEBUG ("rrdtool plugin: queue thread: Wrote %i value%s to %s", + values_num, (values_num == 1) ? "" : "s", + queue_entry->filename); for (i = 0; i < values_num; i++) { @@ -766,7 +768,8 @@ static int rrd_compare_numeric (const void *a_ptr, const void *b_ptr) return (0); } /* int rrd_compare_numeric */ -static int rrd_write (const data_set_t *ds, const value_list_t *vl) +static int rrd_write (const data_set_t *ds, const value_list_t *vl, + user_data_t __attribute__((unused)) *user_data) { struct stat statbuf; char filename[512]; @@ -814,7 +817,8 @@ static int rrd_write (const data_set_t *ds, const value_list_t *vl) return (status); } /* int rrd_write */ -static int rrd_flush (int timeout, const char *identifier) +static int rrd_flush (int timeout, const char *identifier, + user_data_t __attribute__((unused)) *user_data) { pthread_mutex_lock (&cache_lock); @@ -991,14 +995,28 @@ static int rrd_shutdown (void) pthread_cond_signal (&queue_cond); pthread_mutex_unlock (&queue_lock); + if ((queue_thread_running != 0) + && ((queue_head != NULL) || (flushq_head != NULL))) + { + INFO ("rrdtool plugin: Shutting down the queue thread. " + "This may take a while."); + } + else if (queue_thread_running != 0) + { + INFO ("rrdtool plugin: Shutting down the queue thread."); + } + /* Wait for all the values to be written to disk before returning. */ - if (queue_thread != 0) + if (queue_thread_running != 0) { pthread_join (queue_thread, NULL); - queue_thread = 0; + memset (&queue_thread, 0, sizeof (queue_thread)); + queue_thread_running = 0; DEBUG ("rrdtool plugin: queue_thread exited."); } + /* TODO: Maybe it'd be a good idea to free the cache here.. */ + return (0); } /* int rrd_shutdown */ @@ -1043,12 +1061,14 @@ static int rrd_init (void) pthread_mutex_unlock (&cache_lock); - status = pthread_create (&queue_thread, NULL, rrd_queue_thread, NULL); + status = pthread_create (&queue_thread, /* attr = */ NULL, + rrd_queue_thread, /* args = */ NULL); if (status != 0) { ERROR ("rrdtool plugin: Cannot create queue-thread."); return (-1); } + queue_thread_running = 1; DEBUG ("rrdtool plugin: rrd_init: datadir = %s; stepsize = %i;" " heartbeat = %i; rrarows = %i; xff = %lf;", @@ -1066,7 +1086,7 @@ void module_register (void) plugin_register_config ("rrdtool", rrd_config, config_keys, config_keys_num); plugin_register_init ("rrdtool", rrd_init); - plugin_register_write ("rrdtool", rrd_write); - plugin_register_flush ("rrdtool", rrd_flush); + plugin_register_write ("rrdtool", rrd_write, /* user_data = */ NULL); + plugin_register_flush ("rrdtool", rrd_flush, /* user_data = */ NULL); plugin_register_shutdown ("rrdtool", rrd_shutdown); } diff --git a/src/snmp.c b/src/snmp.c index 352075f2..23e199ec 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -70,16 +70,8 @@ struct host_definition_s void *sess_handle; c_complain_t complaint; uint32_t interval; - time_t next_update; data_definition_t **data_list; int data_list_len; - enum /******************************************************/ - { /* This host.. */ - STATE_IDLE, /* - just sits there until `next_update < interval_g' */ - STATE_WAIT, /* - waits to be queried. */ - STATE_BUSY /* - is currently being queried. */ - } state; /******************************************************/ - struct host_definition_s *next; }; typedef struct host_definition_s host_definition_t; @@ -104,20 +96,50 @@ typedef struct csnmp_table_values_s csnmp_table_values_t; /* * Private variables */ -static int do_shutdown = 0; - -pthread_t *threads = NULL; -int threads_num = 0; - static data_definition_t *data_head = NULL; -static host_definition_t *host_head = NULL; -static pthread_mutex_t host_lock = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t host_cond = PTHREAD_COND_INITIALIZER; +/* + * Prototypes + */ +static int csnmp_read_host (user_data_t *ud); /* * Private functions */ +static void csnmp_host_close_session (host_definition_t *host) /* {{{ */ +{ + if (host->sess_handle == NULL) + return; + + snmp_sess_close (host->sess_handle); + host->sess_handle = NULL; +} /* }}} void csnmp_host_close_session */ + +static void csnmp_host_definition_destroy (void *arg) /* {{{ */ +{ + host_definition_t *hd; + + hd = arg; + + if (hd == NULL) + return; + + if (hd->name != NULL) + { + DEBUG ("snmp plugin: Destroying host definition for host `%s'.", + hd->name); + } + + csnmp_host_close_session (hd); + + sfree (hd->name); + sfree (hd->address); + sfree (hd->community); + sfree (hd->data_list); + + sfree (hd); +} /* }}} void csnmp_host_definition_destroy */ + /* Many functions to handle the configuration. {{{ */ /* First there are many functions which do configuration stuff. It's a big * bloated and messy, I'm afraid. */ @@ -543,6 +565,11 @@ static int csnmp_config_add_host (oconfig_item_t *ci) int status = 0; int i; + /* Registration stuff. */ + char cb_name[DATA_MAX_NAME_LEN]; + user_data_t cb_data; + struct timespec cb_interval; + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { WARNING ("snmp plugin: `Host' needs exactly one string argument."); @@ -565,8 +592,6 @@ static int csnmp_config_add_host (oconfig_item_t *ci) hd->sess_handle = NULL; hd->interval = 0; - hd->next_update = 0; - hd->state = STATE_IDLE; for (i = 0; i < ci->children_num; i++) { @@ -613,23 +638,30 @@ static int csnmp_config_add_host (oconfig_item_t *ci) if (status != 0) { - sfree (hd->name); - sfree (hd); + csnmp_host_definition_destroy (hd); return (-1); } DEBUG ("snmp plugin: hd = { name = %s, address = %s, community = %s, version = %i }", hd->name, hd->address, hd->community, hd->version); - if (host_head == NULL) - host_head = hd; - else + ssnprintf (cb_name, sizeof (cb_name), "snmp-%s", hd->name); + + memset (&cb_data, 0, sizeof (cb_data)); + cb_data.data = hd; + cb_data.free_func = csnmp_host_definition_destroy; + + memset (&cb_interval, 0, sizeof (cb_interval)); + if (hd->interval != 0) + cb_interval.tv_sec = (time_t) hd->interval; + + status = plugin_register_complex_read (cb_name, csnmp_read_host, + /* interval = */ &cb_interval, /* user_data = */ &cb_data); + if (status != 0) { - host_definition_t *last; - last = host_head; - while (last->next != NULL) - last = last->next; - last->next = hd; + ERROR ("snmp plugin: Registering complex read function failed."); + csnmp_host_definition_destroy (hd); + return (-1); } return (0); @@ -659,15 +691,6 @@ static int csnmp_config (oconfig_item_t *ci) /* }}} End of the config stuff. Now the interesting part begins */ -static void csnmp_host_close_session (host_definition_t *host) -{ - if (host->sess_handle == NULL) - return; - - snmp_sess_close (host->sess_handle); - host->sess_handle = NULL; -} /* void csnmp_host_close_session */ - static void csnmp_host_open_session (host_definition_t *host) { struct snmp_session sess; @@ -1413,11 +1436,19 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) return (0); } /* int csnmp_read_value */ -static int csnmp_read_host (host_definition_t *host) +static int csnmp_read_host (user_data_t *ud) { - int i; + host_definition_t *host; time_t time_start; time_t time_end; + int status; + int success; + int i; + + host = ud->data; + + if (host->interval == 0) + host->interval = interval_g; time_start = time (NULL); DEBUG ("snmp plugin: csnmp_read_host (%s) started at %u;", host->name, @@ -1429,14 +1460,18 @@ static int csnmp_read_host (host_definition_t *host) if (host->sess_handle == NULL) return (-1); + success = 0; for (i = 0; i < host->data_list_len; i++) { data_definition_t *data = host->data_list[i]; if (data->is_table) - csnmp_read_table (host, data); + status = csnmp_read_table (host, data); else - csnmp_read_value (host, data); + status = csnmp_read_value (host, data); + + if (status == 0) + success++; } time_end = time (NULL); @@ -1444,169 +1479,32 @@ static int csnmp_read_host (host_definition_t *host) (unsigned int) time_end); if ((uint32_t) (time_end - time_start) > host->interval) { - WARNING ("snmp plugin: Host `%s' should be queried every %i seconds, " - "but reading all values takes %u seconds.", + WARNING ("snmp plugin: Host `%s' should be queried every %"PRIu32 + " seconds, but reading all values takes %u seconds.", host->name, host->interval, (unsigned int) (time_end - time_start)); } + if (success == 0) + return (-1); + return (0); } /* int csnmp_read_host */ -static void *csnmp_read_thread (void __attribute__((unused)) *data) -{ - host_definition_t *host; - - pthread_mutex_lock (&host_lock); - while (do_shutdown == 0) - { - pthread_cond_wait (&host_cond, &host_lock); - - for (host = host_head; host != NULL; host = host->next) - { - if (do_shutdown != 0) - break; - if (host->state != STATE_WAIT) - continue; - - host->state = STATE_BUSY; - pthread_mutex_unlock (&host_lock); - csnmp_read_host (host); - pthread_mutex_lock (&host_lock); - host->state = STATE_IDLE; - } /* for (host) */ - } /* while (do_shutdown == 0) */ - pthread_mutex_unlock (&host_lock); - - pthread_exit ((void *) 0); - return ((void *) 0); -} /* void *csnmp_read_thread */ - static int csnmp_init (void) { - host_definition_t *host; - int i; - - if (host_head == NULL) - { - NOTICE ("snmp plugin: No host has been defined."); - return (-1); - } - call_snmp_init_once (); - threads_num = 0; - for (host = host_head; host != NULL; host = host->next) - { - threads_num++; - /* We need to initialize `interval' here, because `interval_g' isn't - * initialized during `configure'. */ - host->next_update = time (NULL); - if (host->interval == 0) - { - host->interval = interval_g; - } - else if (host->interval < (uint32_t) interval_g) - { - host->interval = interval_g; - WARNING ("snmp plugin: Data for host `%s' will be collected every %i seconds.", - host->name, host->interval); - } - - csnmp_host_open_session (host); - } /* for (host) */ - - /* Now start the reading threads */ - if (threads_num > 3) - { - threads_num = 3 + ((threads_num - 3) / 10); - if (threads_num > 10) - threads_num = 10; - } - - threads = (pthread_t *) malloc (threads_num * sizeof (pthread_t)); - if (threads == NULL) - { - ERROR ("snmp plugin: malloc failed."); - return (-1); - } - memset (threads, '\0', threads_num * sizeof (pthread_t)); - - for (i = 0; i < threads_num; i++) - pthread_create (threads + i, NULL, csnmp_read_thread, (void *) 0); - return (0); } /* int csnmp_init */ -static int csnmp_read (void) -{ - host_definition_t *host; - time_t now; - - if (host_head == NULL) - { - INFO ("snmp plugin: No hosts configured."); - return (-1); - } - - now = time (NULL); - - pthread_mutex_lock (&host_lock); - for (host = host_head; host != NULL; host = host->next) - { - if (host->state != STATE_IDLE) - continue; - - /* Skip this host if the next or a later iteration will be sufficient. */ - if (host->next_update >= (now + interval_g)) - continue; - - host->state = STATE_WAIT; - host->next_update = now + host->interval; - } /* for (host) */ - - pthread_cond_broadcast (&host_cond); - pthread_mutex_unlock (&host_lock); - - return (0); -} /* int csnmp_read */ - static int csnmp_shutdown (void) { - host_definition_t *host_this; - host_definition_t *host_next; - data_definition_t *data_this; data_definition_t *data_next; - int i; - - pthread_mutex_lock (&host_lock); - do_shutdown = 1; - pthread_cond_broadcast (&host_cond); - pthread_mutex_unlock (&host_lock); - - for (i = 0; i < threads_num; i++) - pthread_join (threads[i], NULL); - - /* Now that all the threads have exited, let's free all the global variables. - * This isn't really neccessary, I guess, but I think it's good stile to do - * so anyway. */ - host_this = host_head; - host_head = NULL; - while (host_this != NULL) - { - host_next = host_this->next; - - csnmp_host_close_session (host_this); - - sfree (host_this->name); - sfree (host_this->address); - sfree (host_this->community); - sfree (host_this->data_list); - sfree (host_this); - - host_this = host_next; - } + /* When we get here, the read threads have been stopped and all the + * `host_definition_t' will be freed. */ + DEBUG ("snmp plugin: Destroying all data definitions."); data_this = data_head; data_head = NULL; @@ -1629,7 +1527,6 @@ void module_register (void) { plugin_register_complex_config ("snmp", csnmp_config); plugin_register_init ("snmp", csnmp_init); - plugin_register_read ("snmp", csnmp_read); plugin_register_shutdown ("snmp", csnmp_shutdown); } /* void module_register */ diff --git a/src/syslog.c b/src/syslog.c index a21bef18..ace9dc6f 100644 --- a/src/syslog.c +++ b/src/syslog.c @@ -68,7 +68,8 @@ static int sl_config (const char *key, const char *value) return (0); } /* int sl_config */ -static void sl_log (int severity, const char *msg) +static void sl_log (int severity, const char *msg, + user_data_t __attribute__((unused)) *user_data) { if (severity > log_level) return; @@ -88,6 +89,6 @@ void module_register (void) openlog ("collectd", LOG_CONS | LOG_PID, LOG_DAEMON); plugin_register_config ("syslog", sl_config, config_keys, config_keys_num); - plugin_register_log ("syslog", sl_log); + plugin_register_log ("syslog", sl_log, /* user_data = */ NULL); plugin_register_shutdown ("syslog", sl_shutdown); } /* void module_register(void) */ diff --git a/src/table.c b/src/table.c new file mode 100644 index 00000000..2911bf02 --- /dev/null +++ b/src/table.c @@ -0,0 +1,560 @@ +/** + * collectd - src/table.c + * Copyright (C) 2009 Sebastian Harl + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Sebastian Harl + **/ + +/* + * This module provides generic means to parse and dispatch tabular data. + */ + +#include "collectd.h" +#include "common.h" + +#include "configfile.h" +#include "plugin.h" + +#define log_err(...) ERROR ("table plugin: " __VA_ARGS__) +#define log_warn(...) WARNING ("table plugin: " __VA_ARGS__) + +/* + * private data types + */ + +typedef struct { + char *type; + char *instance_prefix; + int *instances; + size_t instances_num; + int *values; + size_t values_num; + + const data_set_t *ds; +} tbl_result_t; + +typedef struct { + char *file; + char *sep; + char *instance; + + tbl_result_t *results; + size_t results_num; + + size_t max_colnum; +} tbl_t; + +static void tbl_result_setup (tbl_result_t *res) +{ + res->type = NULL; + + res->instance_prefix = NULL; + res->instances = NULL; + res->instances_num = 0; + + res->values = NULL; + res->values_num = 0; + + res->ds = NULL; +} /* tbl_result_setup */ + +static void tbl_result_clear (tbl_result_t *res) +{ + sfree (res->type); + + sfree (res->instance_prefix); + sfree (res->instances); + res->instances_num = 0; + + sfree (res->values); + res->values_num = 0; + + res->ds = NULL; +} /* tbl_result_clear */ + +static void tbl_setup (tbl_t *tbl, char *file) +{ + tbl->file = sstrdup (file); + tbl->sep = NULL; + tbl->instance = NULL; + + tbl->results = NULL; + tbl->results_num = 0; + + tbl->max_colnum = 0; +} /* tbl_setup */ + +static void tbl_clear (tbl_t *tbl) +{ + size_t i; + + sfree (tbl->file); + sfree (tbl->sep); + sfree (tbl->instance); + + for (i = 0; i < tbl->results_num; ++i) + tbl_result_clear (tbl->results + i); + sfree (tbl->results); + tbl->results_num = 0; + + tbl->max_colnum = 0; +} /* tbl_clear */ + +static tbl_t *tables; +static size_t tables_num; + +/* + * configuration handling + */ + +static int tbl_config_set_s (char *name, char **var, oconfig_item_t *ci) +{ + if ((1 != ci->values_num) + || (OCONFIG_TYPE_STRING != ci->values[0].type)) { + log_err ("\"%s\" expects a single string argument.", name); + return 1; + } + + sfree (*var); + *var = sstrdup (ci->values[0].value.string); + return 0; +} /* tbl_config_set_separator */ + +static int tbl_config_append_array_i (char *name, int **var, size_t *len, + oconfig_item_t *ci) +{ + int *tmp; + + size_t i; + + if (1 > ci->values_num) { + log_err ("\"%s\" expects at least one argument.", name); + return 1; + } + + for (i = 0; i < ci->values_num; ++i) { + if (OCONFIG_TYPE_NUMBER != ci->values[i].type) { + log_err ("\"%s\" expects numerical arguments only.", name); + return 1; + } + } + + *len += ci->values_num; + tmp = (int *)realloc (*var, *len * sizeof (**var)); + if (NULL == tmp) { + char errbuf[1024]; + log_err ("realloc failed: %s.", + sstrerror (errno, errbuf, sizeof (errbuf))); + return -1; + } + + *var = tmp; + + for (i = *len - ci->values_num; i < *len; ++i) + (*var)[i] = (int)ci->values[i].value.number; + return 0; +} /* tbl_config_append_array_s */ + +static int tbl_config_result (tbl_t *tbl, oconfig_item_t *ci) +{ + tbl_result_t *res; + + int status = 0; + size_t i; + + if (0 != ci->values_num) { + log_err (" does not expect any arguments."); + return 1; + } + + res = (tbl_result_t *)realloc (tbl->results, + (tbl->results_num + 1) * sizeof (*tbl->results)); + if (NULL == tbl) { + char errbuf[1024]; + log_err ("realloc failed: %s.", + sstrerror (errno, errbuf, sizeof (errbuf))); + return -1; + } + + tbl->results = res; + ++tbl->results_num; + + res = tbl->results + tbl->results_num - 1; + tbl_result_setup (res); + + for (i = 0; i < ci->children_num; ++i) { + oconfig_item_t *c = ci->children + i; + + if (0 == strcasecmp (c->key, "Type")) + tbl_config_set_s (c->key, &res->type, c); + else if (0 == strcasecmp (c->key, "InstancePrefix")) + tbl_config_set_s (c->key, &res->instance_prefix, c); + else if (0 == strcasecmp (c->key, "InstancesFrom")) + tbl_config_append_array_i (c->key, + &res->instances, &res->instances_num, c); + else if (0 == strcasecmp (c->key, "ValuesFrom")) + tbl_config_append_array_i (c->key, + &res->values, &res->values_num, c); + else + log_warn ("Ignoring unknown config key \"%s\" " + " in .", c->key); + } + + if (NULL == res->type) { + log_err ("No \"Type\" option specified for " + "in table \"%s\".", tbl->file); + status = 1; + } + + if (NULL == res->values) { + log_err ("No \"ValuesFrom\" option specified for " + "in table \"%s\".", tbl->file); + status = 1; + } + + if (0 != status) { + tbl_result_clear (res); + --tbl->results_num; + return status; + } + return 0; +} /* tbl_config_result */ + +static int tbl_config_table (oconfig_item_t *ci) +{ + tbl_t *tbl; + + int status = 0; + size_t i; + + if ((1 != ci->values_num) + || (OCONFIG_TYPE_STRING != ci->values[0].type)) { + log_err ("
expects a single string argument."); + return 1; + } + + tbl = (tbl_t *)realloc (tables, (tables_num + 1) * sizeof (*tables)); + if (NULL == tbl) { + char errbuf[1024]; + log_err ("realloc failed: %s.", + sstrerror (errno, errbuf, sizeof (errbuf))); + return -1; + } + + tables = tbl; + ++tables_num; + + tbl = tables + tables_num - 1; + tbl_setup (tbl, ci->values[0].value.string); + + for (i = 0; i < ci->children_num; ++i) { + oconfig_item_t *c = ci->children + i; + + if (0 == strcasecmp (c->key, "Separator")) + tbl_config_set_s (c->key, &tbl->sep, c); + else if (0 == strcasecmp (c->key, "Instance")) + tbl_config_set_s (c->key, &tbl->instance, c); + else if (0 == strcasecmp (c->key, "Result")) + tbl_config_result (tbl, c); + else + log_warn ("Ignoring unknown config key \"%s\" " + "in
.", c->key, tbl->file); + } + + if (NULL == tbl->sep) { + log_err ("Table \"%s\" does not specify any separator.", tbl->file); + status = 1; + } + strunescape (tbl->sep, strlen (tbl->sep) + 1); + + if (NULL == tbl->instance) { + tbl->instance = sstrdup (tbl->file); + replace_special (tbl->instance, strlen (tbl->instance)); + } + + if (NULL == tbl->results) { + log_err ("Table \"%s\" does not specify any (valid) results.", + tbl->file); + status = 1; + } + + if (0 != status) { + tbl_clear (tbl); + --tables_num; + return status; + } + + for (i = 0; i < tbl->results_num; ++i) { + tbl_result_t *res = tbl->results + i; + size_t j; + + for (j = 0; j < res->instances_num; ++j) + if (res->instances[j] > tbl->max_colnum) + tbl->max_colnum = res->instances[j]; + + for (j = 0; j < res->values_num; ++j) + if (res->values[j] > tbl->max_colnum) + tbl->max_colnum = res->values[j]; + } + return 0; +} /* tbl_config_table */ + +static int tbl_config (oconfig_item_t *ci) +{ + size_t i; + + for (i = 0; i < ci->children_num; ++i) { + oconfig_item_t *c = ci->children + i; + + if (0 == strcasecmp (c->key, "Table")) + tbl_config_table (c); + else + log_warn ("Ignoring unknown config key \"%s\".", c->key); + } + return 0; +} /* tbl_config */ + +/* + * result handling + */ + +static int tbl_prepare (tbl_t *tbl) +{ + size_t i; + + for (i = 0; i < tbl->results_num; ++i) { + tbl_result_t *res = tbl->results + i; + + res->ds = plugin_get_ds (res->type); + if (NULL == res->ds) { + log_err ("Unknown type \"%s\". See types.db(5) for details.", + res->type); + return -1; + } + + if (res->values_num != (size_t)res->ds->ds_num) { + log_err ("Invalid type \"%s\". Expected %zu data source%s, " + "got %i.", res->type, res->values_num, + (1 == res->values_num) ? "" : "s", + res->ds->ds_num); + return -1; + } + } + return 0; +} /* tbl_prepare */ + +static int tbl_finish (tbl_t *tbl) +{ + size_t i; + + for (i = 0; i < tbl->results_num; ++i) + tbl->results[i].ds = NULL; + return 0; +} /* tbl_finish */ + +static int tbl_result_dispatch (tbl_t *tbl, tbl_result_t *res, + char **fields, size_t fields_num) +{ + value_list_t vl = VALUE_LIST_INIT; + value_t values[res->values_num]; + + size_t i; + + assert (NULL != res->ds); + assert (res->values_num == res->ds->ds_num); + + for (i = 0; i < res->values_num; ++i) { + char *value; + + assert (res->values[i] < fields_num); + value = fields[res->values[i]]; + + if (0 != parse_value (value, &values[i], res->ds->ds[i])) + return -1; + } + + vl.values = values; + vl.values_len = STATIC_ARRAY_SIZE (values); + + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "table", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, tbl->instance, sizeof (vl.plugin_instance)); + sstrncpy (vl.type, res->type, sizeof (vl.type)); + + if (0 == res->instances_num) { + if (NULL != res->instance_prefix) + sstrncpy (vl.type_instance, res->instance_prefix, + sizeof (vl.type_instance)); + } + else { + char *instances[res->instances_num]; + char instances_str[DATA_MAX_NAME_LEN]; + + for (i = 0; i < res->instances_num; ++i) { + assert (res->instances[i] < fields_num); + instances[i] = fields[res->instances[i]]; + } + + strjoin (instances_str, sizeof (instances_str), + instances, STATIC_ARRAY_SIZE (instances), "-"); + instances_str[sizeof (instances_str) - 1] = '\0'; + + vl.type_instance[sizeof (vl.type_instance) - 1] = '\0'; + if (NULL == res->instance_prefix) + strncpy (vl.type_instance, instances_str, + sizeof (vl.type_instance)); + else + snprintf (vl.type_instance, sizeof (vl.type_instance), + "%s-%s", res->instance_prefix, instances_str); + + if ('\0' != vl.type_instance[sizeof (vl.type_instance) - 1]) { + vl.type_instance[sizeof (vl.type_instance) - 1] = '\0'; + log_warn ("Truncated type instance: %s.", vl.type_instance); + } + } + + plugin_dispatch_values (&vl); + return 0; +} /* tbl_result_dispatch */ + +static int tbl_parse_line (tbl_t *tbl, char *line, size_t len) +{ + char *fields[tbl->max_colnum + 1]; + char *ptr, *saveptr; + + size_t i; + + i = 0; + ptr = line; + saveptr = NULL; + while (NULL != (fields[i] = strtok_r (ptr, tbl->sep, &saveptr))) { + ptr = NULL; + ++i; + + if (i > tbl->max_colnum) + break; + } + + if (i <= tbl->max_colnum) { + log_err ("Not enough columns in line " + "(expected at least %zu, got %zu).", + tbl->max_colnum + 1, i); + return -1; + } + + for (i = 0; i < tbl->results_num; ++i) + if (0 != tbl_result_dispatch (tbl, tbl->results + i, + fields, STATIC_ARRAY_SIZE (fields))) { + log_err ("Failed to dispatch result."); + continue; + } + return 0; +} /* tbl_parse_line */ + +static int tbl_read_table (tbl_t *tbl) +{ + FILE *fh; + char buf[4096]; + + fh = fopen (tbl->file, "r"); + if (NULL == fh) { + char errbuf[1024]; + log_err ("Failed to open file \"%s\": %s.", tbl->file, + sstrerror (errno, errbuf, sizeof (errbuf))); + return -1; + } + + buf[sizeof (buf) - 1] = '\0'; + while (NULL != fgets (buf, sizeof (buf), fh)) { + if ('\0' != buf[sizeof (buf) - 1]) { + buf[sizeof (buf) - 1] = '\0'; + log_err ("Table %s: Truncated line: %s", tbl->file, buf); + } + + if (0 != tbl_parse_line (tbl, buf, sizeof (buf))) { + log_err ("Table %s: Failed to parse line: %s", tbl->file, buf); + continue; + } + } + + if (0 != ferror (fh)) { + char errbuf[1024]; + log_err ("Failed to read from file \"%s\": %s.", tbl->file, + sstrerror (errno, errbuf, sizeof (errbuf))); + fclose (fh); + return -1; + } + + fclose (fh); + return 0; +} /* tbl_read_table */ + +/* + * collectd callbacks + */ + +static int tbl_read (void) +{ + int status = -1; + size_t i; + + if (0 == tables_num) + return 0; + + for (i = 0; i < tables_num; ++i) { + tbl_t *tbl = tables + i; + + if (0 != tbl_prepare (tbl)) { + log_err ("Failed to prepare and parse table \"%s\".", tbl->file); + continue; + } + + if (0 == tbl_read_table (tbl)) + status = 0; + + tbl_finish (tbl); + } + return status; +} /* tbl_read */ + +static int tbl_shutdown (void) +{ + size_t i; + + for (i = 0; i < tables_num; ++i) + tbl_clear (&tables[i]); + sfree (tables); + return 0; +} /* tbl_shutdown */ + +static int tbl_init (void) +{ + if (0 == tables_num) + return 0; + + plugin_register_read ("table", tbl_read); + plugin_register_shutdown ("table", tbl_shutdown); + return 0; +} /* tbl_init */ + +void module_register (void) +{ + plugin_register_complex_config ("table", tbl_config); + plugin_register_init ("table", tbl_init); +} /* module_register */ + +/* vim: set sw=4 ts=4 tw=78 noexpandtab : */ diff --git a/src/ted.c b/src/ted.c new file mode 100644 index 00000000..8dc00e5a --- /dev/null +++ b/src/ted.c @@ -0,0 +1,360 @@ +/** + * collectd - src/ted.c + * Copyright (C) 2009 Eric Reed + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Eric Reed + * + * This is a collectd module for The Energy Detective: A low-cost whole + * house energy monitoring system. For more information on TED, see + * http://theenergydetective.com + * + * This module was not created by Energy, Inc. nor is it supported by + * them in any way. It was created using information from two sources: + * David Satterfield's TED module for Misterhouse, and Micah Dowty's TED + * Python Module. + * + * This has only tested with the model 1001 RDU, with + * firmware version 9.01U. The USB port is uses the very common FTDI + * USB-to-serial chip, so the RDU will show up as a serial device on + * Windows, Mac OS, or Linux. + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" + +#if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H && HAVE_MATH_H +# include +# include +# include +#else +# error "No applicable input method." +#endif + +#define EXPECTED_PACKAGE_LENGTH 278 +#define ESCAPE 0x10 +#define PKT_BEGIN 0x04 +#define PKT_END 0x03 + +#define DEFAULT_DEVICE "/dev/ttyUSB0" + +static char *conf_device = NULL; +static int conf_retries = 0; + +static int fd = -1; + +static const char *config_keys[] = +{ + "Device", + "Retries" +}; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +static int ted_read_value(double *ret_power, double *ret_voltage) +{ + unsigned char receive_buffer[300]; + unsigned char package_buffer[300]; + char pkt_request[1] = {0xAA}; + int package_buffer_pos; + + fd_set input; + struct timeval timeout; + + int end_flag; + int escape_flag; + + int status; + + assert (fd >= 0); + + /* Initialize the input set*/ + FD_ZERO (&input); + FD_SET (fd, &input); + + /* Initialize timeout structure, set to 2 seconds */ + memset (&timeout, 0, sizeof (timeout)); + timeout.tv_sec = 2; + timeout.tv_usec = 0; + + /* clear out anything in the buffer */ + tcflush (fd, TCIFLUSH); + + status = write (fd, pkt_request, sizeof(pkt_request)); + if (status <= 0) + { + ERROR ("ted plugin: swrite failed."); + return (-1); + } + + /* Loop until we find the end of the package */ + end_flag = 0; + escape_flag = 0; + package_buffer_pos = 0; + while (end_flag == 0) + { + ssize_t receive_buffer_length; + ssize_t i; + + /* check for timeout or input error*/ + status = select (fd + 1, &input, NULL, NULL, &timeout); + if (status == 0) /* Timeout */ + { + WARNING ("ted plugin: Timeout while waiting for file descriptor " + "to become ready."); + return (-1); + } + else if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR))) + { + /* Some signal or something. Start over.. */ + continue; + } + else if (status < 0) + { + char errbuf[1024]; + ERROR ("ted plugin: select failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + receive_buffer_length = read (fd, receive_buffer, sizeof (receive_buffer)); + if (receive_buffer_length < 0) + { + char errbuf[1024]; + if ((errno == EAGAIN) || (errno == EINTR)) + continue; + ERROR ("ted plugin: read(2) failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + else if (receive_buffer_length == 0) + { + /* Should we close the FD in this case? */ + WARNING ("ted plugin: Received EOF from file descriptor."); + return (-1); + } + else if (receive_buffer_length > sizeof (receive_buffer)) + { + ERROR ("ted plugin: read(2) returned invalid value %zi.", + receive_buffer_length); + return (-1); + } + + /* + * packet filter loop + * + * Handle escape sequences in `receive_buffer' and put the + * result in `package_buffer'. + */ + /* We need to see the begin sequence first. When we receive `ESCAPE + * PKT_BEGIN', we set `package_buffer_pos' to zero to signal that + * the beginning of the package has been found. */ + + escape_flag = 0; + for (i = 0; i < receive_buffer_length; i++) + { + /* Check if previous byte was the escape byte. */ + if (escape_flag == 1) + { + escape_flag = 0; + /* escape escape = single escape */ + if ((receive_buffer[i] == ESCAPE) + && (package_buffer_pos >= 0)) + { + package_buffer[package_buffer_pos] = ESCAPE; + package_buffer_pos++; + } + else if (receive_buffer[i] == PKT_BEGIN) + { + package_buffer_pos = 0; + } + else if (receive_buffer[i] == PKT_END) + { + end_flag = 1; + break; + } + else + { + DEBUG ("ted plugin: Unknown escaped byte: %#x", + (unsigned int) receive_buffer[i]); + } + } + else if (receive_buffer[i] == ESCAPE) + { + escape_flag = 1; + } + /* if we are in a package add byte to buffer + * otherwise throw away */ + else if (package_buffer_pos >= 0) + { + package_buffer[package_buffer_pos] = receive_buffer[i]; + package_buffer_pos++; + } + } /* for (i = 0; i < receive_buffer_length; i++) */ + } /* while (end_flag == 0) */ + + /* Check for errors inside the loop. */ + if ((end_flag == 0) || (package_buffer_pos != EXPECTED_PACKAGE_LENGTH)) + return (-1); + + /* + * Power is at positions 247 and 248 (LSB first) in [10kW]. + * Voltage is at positions 251 and 252 (LSB first) in [.1V]. + * + * Power is in 10 Watt steps + * Voltage is in volts + */ + *ret_power = 10.0 * (double) ((((int) package_buffer[248]) * 256) + + ((int) package_buffer[247])); + *ret_voltage = 0.1 * (double) ((((int) package_buffer[252]) * 256) + + ((int) package_buffer[251])); + + /* success */ + return (0); +} /* int ted_read_value */ + +static int ted_open_device (void) +{ + const char *dev; + struct termios options; + + if (fd >= 0) + return (0); + + dev = DEFAULT_DEVICE; + if (conf_device != NULL) + dev = conf_device; + + fd = open (dev, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if (fd < 0) + { + ERROR ("ted plugin: Unable to open device %s.", dev); + return (-1); + } + + /* Get the current options for the port... */ + tcgetattr(fd, &options); + options.c_cflag = B19200 | CS8 | CSTOPB | CREAD | CLOCAL; + options.c_iflag = IGNBRK | IGNPAR; + options.c_oflag = 0; + options.c_lflag = 0; + options.c_cc[VTIME] = 20; + options.c_cc[VMIN] = 250; + + /* Set the new options for the port... */ + tcflush(fd, TCIFLUSH); + tcsetattr(fd, TCSANOW, &options); + + INFO ("ted plugin: Successfully opened %s.", dev); + return (0); +} /* int ted_open_device */ + +static void ted_submit (char *type, double value) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].gauge = value; + + vl.time = time (NULL); + vl.values = values; + vl.values_len = 1; + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "ted", sizeof (vl.plugin)); + sstrncpy (vl.type, type, sizeof (vl.type)); + + plugin_dispatch_values (&vl); +} + +static int ted_config (const char *key, const char *value) +{ + if (strcasecmp ("Device", key) == 0) + { + sfree (conf_device); + conf_device = sstrdup (value); + } + else if (strcasecmp ("Retries", key) == 0) + { + int tmp; + + tmp = atoi (value); + if (tmp < 0) + { + WARNING ("ted plugin: Invalid retry count: %i", tmp); + return (1); + } + conf_retries = tmp; + } + else + { + ERROR ("ted plugin: Unknown config option: %s", key); + return (-1); + } + + return (0); +} /* int ted_config */ + +static int ted_read (void) +{ + double power; + double voltage; + int status; + int i; + + status = ted_open_device (); + if (status != 0) + return (-1); + + power = NAN; + voltage = NAN; + for (i = 0; i <= conf_retries; i++) + { + status = ted_read_value (&power, &voltage); + if (status == 0) + break; + } + + if (status != 0) + return (-1); + + ted_submit ("power", power); + ted_submit ("voltage", voltage); + + return (0); +} /* int ted_read */ + +static int ted_shutdown (void) +{ + if (fd >= 0) + { + close (fd); + fd = -1; + } + + return (0); +} /* int ted_shutdown */ + +void module_register (void) +{ + plugin_register_config ("ted", ted_config, + config_keys, config_keys_num); + plugin_register_read ("ted", ted_read); + plugin_register_shutdown ("ted", ted_shutdown); +} /* void module_register */ + +/* vim: set sw=4 et : */ diff --git a/src/types.db b/src/types.db index 6fc8e113..b38c207a 100644 --- a/src/types.db +++ b/src/types.db @@ -69,6 +69,7 @@ memory value:GAUGE:0:281474976710656 multimeter value:GAUGE:U:U mysql_commands value:COUNTER:0:U mysql_handler value:COUNTER:0:U +mysql_log_position value:COUNTER:0:4294967295 mysql_octets rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295 mysql_qcache hits:COUNTER:0:U, inserts:COUNTER:0:U, not_cached:COUNTER:0:U, lowmem_prunes:COUNTER:0:U, queries_in_cache:GAUGE:0:U mysql_threads running:GAUGE:0:U, connected:GAUGE:0:U, cached:GAUGE:0:U, created:COUNTER:0:U @@ -85,8 +86,11 @@ pg_numbackends value:GAUGE:0:U pg_scan value:COUNTER:0:U pg_xact value:COUNTER:0:U ping ping:GAUGE:0:65535 +ping_droprate value:GAUGE:0:100 +ping_stddev value:GAUGE:0:65535 players value:GAUGE:0:1000000 power value:GAUGE:0:U +protocol_counter value:COUNTER:0:U ps_count processes:GAUGE:0:1000000, threads:GAUGE:0:1000000 ps_cputime user:COUNTER:0:16000000, syst:COUNTER:0:16000000 ps_pagefaults minflt:COUNTER:0:9223372036854775807, majflt:COUNTER:0:9223372036854775807 @@ -107,6 +111,7 @@ temperature value:GAUGE:-273.15:U time_dispersion seconds:GAUGE:-1000000:1000000 timeleft timeleft:GAUGE:0:3600 time_offset seconds:GAUGE:-1000000:1000000 +uptime value:GAUGE:0:4294967295 users users:GAUGE:0:65535 virt_cpu_total ns:COUNTER:0:256000000000 virt_vcpu ns:COUNTER:0:1000000000 diff --git a/src/types.db.pod b/src/types.db.pod index 4a1bfa40..a46eb41c 100644 --- a/src/types.db.pod +++ b/src/types.db.pod @@ -44,6 +44,9 @@ For example: TypesDB "/opt/collectd/share/collectd/types.db" TypesDB "/opt/collectd/etc/types.db.custom" +B: Make sure to make this file available on all systems if you're +sending values over the network. + =head1 SEE ALSO L, diff --git a/src/uptime.c b/src/uptime.c new file mode 100644 index 00000000..4edfa840 --- /dev/null +++ b/src/uptime.c @@ -0,0 +1,218 @@ +/** + * collectd - src/uptime.c + * Copyright (C) 2009 Marco Chiappero + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Marco Chiappero + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" + +#if KERNEL_LINUX +# define UPTIME_FILE "/proc/uptime" +/* No need for includes, using /proc filesystem, Linux only. */ +/* #endif KERNEL_LINUX */ + +#elif HAVE_LIBKSTAT +/* Using kstats chain to retrieve the boot time, this applies to: + * - Solaris / OpenSolaris + */ +/* #endif HAVE_LIBKSTAT */ + +#elif HAVE_SYS_SYSCTL_H +# include +/* Using sysctl interface to retrieve the boot time, this applies to: + * - *BSD + * - Darwin / OS X + */ +/* #endif HAVE_SYS_SYSCTL_H */ + +#else +# error "No applicable input method." +#endif + +/* + * Global variables + */ +#if KERNEL_LINUX +/* global variables not needed */ +/* #endif KERNEL_LINUX */ + +#elif HAVE_LIBKSTAT +static time_t boottime; +extern kstat_ctl_t *kc; +/* #endif HAVE_LIBKSTAT */ + +#elif HAVE_SYS_SYSCTL_H +static time_t boottime; +#endif + +static void uptime_submit (gauge_t uptime) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].gauge = uptime; + + vl.values = values; + vl.values_len = 1; + + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "uptime", sizeof (vl.plugin)); + sstrncpy (vl.type, "uptime", sizeof (vl.type)); + + plugin_dispatch_values (&vl); +} + +#if !defined(KERNEL_LINUX) || !KERNEL_LINUX +static int uptime_init (void) +{ +/* NOTE + + On unix systems other than Linux there is no /proc filesystem which + calculates the uptime every time we call a read for the /proc/uptime + file, the only information available is the boot time (in unix time, + since epoch). Hence there is no need to read, every time the + plugin_read is called, a value that won't change: this is a right + task for the uptime_init function. However, since uptime_init is run + only once, if the function fails in retrieving the boot time, the + plugin is unregistered and there is no chance to try again later. + Nevertheless, this is very unlikely to happen. + */ + +# if HAVE_LIBKSTAT + kstat_t *ksp; + kstat_named_t *knp; + + ksp = NULL; + knp = NULL; + + /* kstats chain already opened by update_kstat (using *kc), let's verify everything went fine. */ + if (kc == NULL) + { + ERROR ("uptime plugin: kstat chain control structure not available."); + return (-1); + } + + ksp = kstat_lookup (kc, "unix", 0, "system_misc"); + if (ksp == NULL) + { + ERROR ("uptime plugin: Cannot find unix:0:system_misc kstat."); + return (-1); + } + + if (kstat_read (kc, ksp, NULL) < 0) + { + ERROR ("uptime plugin: kstat_read failed."); + return (-1); + } + + knp = (kstat_named_t *) kstat_data_lookup (ksp, "boot_time"); + if (knp == NULL) + { + ERROR ("uptime plugin: kstat_data_lookup (boot_time) failed."); + return (-1); + } + + boottime = (time_t) knp->value.ui32; +/* #endif HAVE_LIBKSTAT */ + +# elif HAVE_SYS_SYSCTL_H + struct timeval boottv; + size_t boottv_len; + int status; + + int mib[2]; + + mib[0] = CTL_KERN; + mib[1] = KERN_BOOTTIME; + + memset (&boottv, 0, sizeof (boottv)); + boottv_len = sizeof (boottv); + + status = sysctl (mib, STATIC_ARRAY_SIZE (mib), &boottv, &boottv_len, + /* new_value = */ NULL, /* new_length = */ 0); + if (status != 0) + { + char errbuf[1024]; + ERROR ("uptime plugin: No value read from sysctl interface: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + boottime = boottv.tv_sec; + if (boottime == 0) + { + ERROR ("uptime plugin: sysctl(3) returned success, " + "but `boottime' is zero!"); + return (-1); + } +#endif /* HAVE_SYS_SYSCTL_H */ + + return (0); + +} +#endif /* !KERNEL_LINUX */ + +static int uptime_read (void) +{ + gauge_t uptime; + +#if KERNEL_LINUX + FILE *fh; + + fh = fopen (UPTIME_FILE, "r"); + + if (fh == NULL) + { + char errbuf[1024]; + ERROR ("uptime plugin: Cannot open "UPTIME_FILE": %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + if ( fscanf (fh, "%lf", &uptime) < 1 ) + { + WARNING ("uptime plugin: No value read from "UPTIME_FILE); + fclose (fh); + return (-1); + } + + fclose (fh); +/* #endif KERNEL_LINUX */ + +#elif HAVE_LIBKSTAT || HAVE_SYS_SYSCTL_H + time_t elapsed; + + elapsed = time (NULL) - boottime; + + uptime = (gauge_t) elapsed; +#endif /* HAVE_LIBKSTAT || HAVE_SYS_SYSCTL_H */ + + uptime_submit (uptime); + + return (0); +} + +void module_register (void) +{ +#if !defined(KERNEL_LINUX) || !KERNEL_LINUX + plugin_register_init ("uptime", uptime_init); +#endif + plugin_register_read ("uptime", uptime_read); +} /* void module_register */ diff --git a/src/utils_cmd_putval.c b/src/utils_cmd_putval.c index 5bd6ec73..826e1b03 100644 --- a/src/utils_cmd_putval.c +++ b/src/utils_cmd_putval.c @@ -1,6 +1,6 @@ /** * collectd - src/utils_cms_putval.c - * Copyright (C) 2007,2008 Florian octo Forster + * Copyright (C) 2007-2009 Florian octo Forster * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -33,7 +33,7 @@ return -1; \ } -static int parse_value (const data_set_t *ds, value_list_t *vl, +static int dispatch_values (const data_set_t *ds, value_list_t *vl, FILE *fh, char *buffer) { char *dummy; @@ -65,12 +65,13 @@ static int parse_value (const data_set_t *ds, value_list_t *vl, break; } - if (strcmp (ptr, "U") == 0) + if ((strcmp (ptr, "U") == 0) && (ds->ds[i].type == DS_TYPE_GAUGE)) vl->values[i].gauge = NAN; - else if (ds->ds[i].type == DS_TYPE_COUNTER) - vl->values[i].counter = atoll (ptr); - else if (ds->ds[i].type == DS_TYPE_GAUGE) - vl->values[i].gauge = atof (ptr); + else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i])) + { + print_to_socket (fh, "-1 Failed to parse value `%s'.\n", ptr); + return (-1); + } i++; } /* while (strtok_r) */ @@ -79,7 +80,7 @@ static int parse_value (const data_set_t *ds, value_list_t *vl, { char identifier[128]; FORMAT_VL (identifier, sizeof (identifier), vl, ds); - ERROR ("cmd putval: parse_value: " + ERROR ("cmd putval: dispatch_values: " "Number of values incorrect: " "Got %i, expected %i. Identifier is `%s'.", i, vl->values_len, identifier); @@ -91,7 +92,7 @@ static int parse_value (const data_set_t *ds, value_list_t *vl, plugin_dispatch_values (vl); return (0); -} /* int parse_value */ +} /* int dispatch_values */ static int set_option (value_list_t *vl, const char *key, const char *value) { @@ -252,7 +253,7 @@ int handle_putval (FILE *fh, char *buffer) } assert (string != NULL); - status = parse_value (ds, &vl, fh, string); + status = dispatch_values (ds, &vl, fh, string); if (status != 0) { /* An error has already been printed. */ diff --git a/src/utils_db_query.c b/src/utils_db_query.c index c2897c7c..5531b25f 100644 --- a/src/utils_db_query.c +++ b/src/utils_db_query.c @@ -197,7 +197,7 @@ static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */ { value_list_t vl = VALUE_LIST_INIT; value_t value; - char *endptr; + char *value_str; assert (r->legacy_mode == 1); assert (r->ds != NULL); @@ -206,23 +206,14 @@ static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */ vl.values = &value; vl.values_len = 1; - endptr = NULL; - errno = 0; - if (r->ds->ds[0].type == DS_TYPE_COUNTER) - vl.values[0].counter = (counter_t) strtoll (column_values[r->legacy_position], - &endptr, /* base = */ 0); - else if (r->ds->ds[0].type == DS_TYPE_GAUGE) - vl.values[0].gauge = (gauge_t) strtod (column_values[r->legacy_position], - &endptr); - else - errno = EINVAL; - - if ((endptr == column_values[r->legacy_position]) || (errno != 0)) + value_str = column_values[r->legacy_position]; + if (0 != parse_value (value_str, &vl.values[0], r->ds->ds[0])) { - WARNING ("db query utils: udb_result_submit: Parsing `%s' as %s failed.", - column_values[r->legacy_position], + ERROR ("db query utils: udb_legacy_result_handle_result: " + "Parsing `%s' as %s failed.", value_str, (r->ds->ds[0].type == DS_TYPE_COUNTER) ? "counter" : "gauge"); - vl.values[0].gauge = NAN; + errno = EINVAL; + return (-1); } sstrncpy (vl.host, q->host, sizeof (vl.host)); @@ -351,7 +342,7 @@ static int udb_legacy_result_create (const char *query_name, /* {{{ */ /* * Result private functions */ -static void udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */ +static int udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */ { value_list_t vl = VALUE_LIST_INIT; size_t i; @@ -365,30 +356,21 @@ static void udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */ if (vl.values == NULL) { ERROR ("db query utils: malloc failed."); - return; + return (-1); } vl.values_len = r->ds->ds_num; for (i = 0; i < r->values_num; i++) { - char *endptr; - - endptr = NULL; - errno = 0; - if (r->ds->ds[i].type == DS_TYPE_COUNTER) - vl.values[i].counter = (counter_t) strtoll (r->values_buffer[i], - &endptr, /* base = */ 0); - else if (r->ds->ds[i].type == DS_TYPE_GAUGE) - vl.values[i].gauge = (gauge_t) strtod (r->values_buffer[i], &endptr); - else - errno = EINVAL; + char *value_str = r->values_buffer[i]; - if ((endptr == r->values_buffer[i]) || (errno != 0)) + if (0 != parse_value (value_str, &vl.values[i], r->ds->ds[i])) { - WARNING ("db query utils: udb_result_submit: Parsing `%s' as %s failed.", - r->values_buffer[i], + ERROR ("db query utils: udb_result_submit: Parsing `%s' as %s failed.", + value_str, (r->ds->ds[i].type == DS_TYPE_COUNTER) ? "counter" : "gauge"); - vl.values[i].gauge = NAN; + errno = EINVAL; + return (-1); } } @@ -430,6 +412,7 @@ static void udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */ plugin_dispatch_values (&vl); sfree (vl.values); + return (0); } /* }}} void udb_result_submit */ static void udb_result_finish_result (udb_result_t *r) /* {{{ */ @@ -468,9 +451,7 @@ static int udb_result_handle_result (udb_result_t *r, /* {{{ */ for (i = 0; i < r->values_num; i++) r->values_buffer[i] = column_values[r->values_pos[i]]; - udb_result_submit (r, q); - - return (0); + return udb_result_submit (r, q); } /* }}} int udb_result_handle_result */ static int udb_result_prepare_result (udb_result_t *r, /* {{{ */ diff --git a/src/utils_heap.c b/src/utils_heap.c new file mode 100644 index 00000000..1ecd07e8 --- /dev/null +++ b/src/utils_heap.c @@ -0,0 +1,223 @@ +/** + * collectd - src/utils_heap.c + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#include +#include +#include +#include +#include + +#include "utils_heap.h" + +struct c_heap_s +{ + pthread_mutex_t lock; + int (*compare) (const void *, const void *); + + void **list; + size_t list_len; /* # entries used */ + size_t list_size; /* # entries allocated */ +}; + +enum reheap_direction +{ + DIR_UP, + DIR_DOWN +}; + +static void reheap (c_heap_t *h, size_t root, enum reheap_direction dir) +{ + size_t left; + size_t right; + size_t min; + int status; + + /* Calculate the positions of the children */ + left = (2 * root) + 1; + if (left >= h->list_len) + left = 0; + + right = (2 * root) + 2; + if (right >= h->list_len) + right = 0; + + /* Check which one of the children is smaller. */ + if ((left == 0) && (right == 0)) + return; + else if (left == 0) + min = right; + else if (right == 0) + min = left; + else + { + status = h->compare (h->list[left], h->list[right]); + if (status > 0) + min = right; + else + min = left; + } + + status = h->compare (h->list[root], h->list[min]); + if (status <= 0) + { + /* We didn't need to change anything, so the rest of the tree should be + * okay now. */ + return; + } + else /* if (status > 0) */ + { + void *tmp; + + tmp = h->list[root]; + h->list[root] = h->list[min]; + h->list[min] = tmp; + } + + if ((dir == DIR_UP) && (root == 0)) + return; + + if (dir == DIR_UP) + reheap (h, root / 2, dir); + else if (dir == DIR_DOWN) + reheap (h, min, dir); +} /* void reheap */ + +c_heap_t *c_heap_create (int (*compare) (const void *, const void *)) +{ + c_heap_t *h; + + if (compare == NULL) + return (NULL); + + h = malloc (sizeof (*h)); + if (h == NULL) + return (NULL); + + memset (h, 0, sizeof (*h)); + pthread_mutex_init (&h->lock, /* attr = */ NULL); + h->compare = compare; + + h->list = NULL; + h->list_len = 0; + h->list_size = 0; + + return (h); +} /* c_heap_t *c_heap_create */ + +void c_heap_destroy (c_heap_t *h) +{ + if (h == NULL) + return; + + h->list_len = 0; + h->list_size = 0; + free (h->list); + h->list = NULL; + + pthread_mutex_destroy (&h->lock); + + free (h); +} /* void c_heap_destroy */ + +int c_heap_insert (c_heap_t *h, void *ptr) +{ + if ((h == NULL) || (ptr == NULL)) + return (-EINVAL); + + pthread_mutex_lock (&h->lock); + + assert (h->list_len <= h->list_size); + if (h->list_len == h->list_size) + { + void **tmp; + + tmp = realloc (h->list, (h->list_size + 16) * sizeof (*h->list)); + if (tmp == NULL) + { + pthread_mutex_unlock (&h->lock); + return (-ENOMEM); + } + + h->list = tmp; + h->list_size += 16; + } + + /* Insert the new node as a leaf. */ + h->list[h->list_len] = ptr; + h->list_len++; + + /* Reorganize the heap from bottom up. */ + reheap (h, /* parent of this node = */ (h->list_len - 1) / 2, DIR_UP); + + pthread_mutex_unlock (&h->lock); + return (0); +} /* int c_heap_insert */ + +void *c_head_get_root (c_heap_t *h) +{ + void *ret = NULL; + + if (h == NULL) + return (NULL); + + pthread_mutex_lock (&h->lock); + + if (h->list_len == 0) + { + pthread_mutex_unlock (&h->lock); + return (NULL); + } + else if (h->list_len == 1) + { + ret = h->list[0]; + h->list[0] = NULL; + h->list_len = 0; + } + else /* if (h->list_len > 1) */ + { + ret = h->list[0]; + h->list[0] = h->list[h->list_len - 1]; + h->list[h->list_len - 1] = NULL; + h->list_len--; + + reheap (h, /* root = */ 0, DIR_DOWN); + } + + /* free some memory */ + if ((h->list_len + 32) < h->list_size) + { + void **tmp; + + tmp = realloc (h->list, (h->list_len + 16) * sizeof (*h->list)); + if (tmp != NULL) + { + h->list = tmp; + h->list_size = h->list_len + 16; + } + } + + pthread_mutex_unlock (&h->lock); + + return (ret); +} /* void *c_head_get_root */ + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/utils_heap.h b/src/utils_heap.h new file mode 100644 index 00000000..dd0f4866 --- /dev/null +++ b/src/utils_heap.h @@ -0,0 +1,96 @@ +/** + * collectd - src/utils_heap.h + * Copyright (C) 2009 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_HEAP_H +#define UTILS_HEAP_H 1 + +struct c_heap_s; +typedef struct c_heap_s c_heap_t; + +/* + * NAME + * c_heap_create + * + * DESCRIPTION + * Allocates a new heap. + * + * PARAMETERS + * `compare' The function-pointer `compare' is used to compare two keys. It + * has to return less than zero if it's first argument is smaller + * then the second argument, more than zero if the first argument + * is bigger than the second argument and zero if they are equal. + * If your keys are char-pointers, you can use the `strcmp' + * function from the libc here. + * + * RETURN VALUE + * A c_heap_t-pointer upon success or NULL upon failure. + */ +c_heap_t *c_heap_create (int (*compare) (const void *, const void *)); + +/* + * NAME + * c_heap_destroy + * + * DESCRIPTION + * Deallocates a heap. Stored value- and key-pointer are lost, but of course + * not freed. + */ +void c_heap_destroy (c_heap_t *h); + +/* + * NAME + * c_heap_insert + * + * DESCRIPTION + * Stores the key-value-pair in the heap pointed to by `h'. + * + * PARAMETERS + * `h' Heap to store the data in. + * `ptr' Value to be stored. This is typically a pointer to a data + * structure. The data structure is of course *not* copied and may + * not be free'd before the pointer has been removed from the heap + * again. + * + * RETURN VALUE + * Zero upon success, non-zero otherwise. It's less than zero if an error + * occurred or greater than zero if the key is already stored in the tree. + */ +int c_heap_insert (c_heap_t *h, void *ptr); + +/* + * NAME + * c_head_get_root + * + * DESCRIPTION + * Removes the value at the root of the heap and returns both, key and value. + * + * PARAMETERS + * `h' Heap to remove key-value-pair from. + * + * RETURN VALUE + * The pointer passed to `c_heap_insert' or NULL if there are no more + * elements in the heap (or an error occurred). + */ +void *c_head_get_root (c_heap_t *h); + +#endif /* UTILS_HEAP_H */ +/* vim: set sw=2 sts=2 et : */