diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2009-11-05 17:21:11 +0100 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2009-11-05 17:21:11 +0100 |
| commit | 64c5d35aebc38f93ce5c086c15c15de5acb21b2f (patch) | |
| tree | 51113a991ff0111cddcf65bca90ac995e2173c1b | |
| parent | 01f36609ba1631751e492784d95b0e6b0706cf45 (diff) | |
InformAllUsers support. enabled by default for Kerneloops. Tested wuth CCpp.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
| -rw-r--r-- | abrt.spec | 1 | ||||
| -rw-r--r-- | inc/abrtlib.h | 1 | ||||
| -rw-r--r-- | lib/Plugins/CCpp.cpp | 4 | ||||
| -rw-r--r-- | lib/Plugins/CCpp.h | 2 | ||||
| -rw-r--r-- | lib/Plugins/Kerneloops.conf | 2 | ||||
| -rw-r--r-- | lib/Plugins/Kerneloops.cpp | 10 | ||||
| -rw-r--r-- | lib/Plugins/Kerneloops.h | 5 | ||||
| -rw-r--r-- | lib/Plugins/KerneloopsReporter.cpp | 15 | ||||
| -rw-r--r-- | lib/Plugins/KerneloopsScanner.cpp | 27 | ||||
| -rw-r--r-- | lib/Plugins/KerneloopsScanner.h | 2 | ||||
| -rw-r--r-- | lib/Plugins/Makefile.am | 62 | ||||
| -rw-r--r-- | lib/Utils/xfuncs.cpp | 13 | ||||
| -rw-r--r-- | src/Applet/Applet.cpp | 23 | ||||
| -rw-r--r-- | src/Daemon/CommLayerServer.h | 2 | ||||
| -rw-r--r-- | src/Daemon/CommLayerServerDBus.cpp | 24 | ||||
| -rw-r--r-- | src/Daemon/CommLayerServerDBus.h | 2 | ||||
| -rw-r--r-- | src/Daemon/Daemon.cpp | 50 | ||||
| -rw-r--r-- | src/Daemon/abrt.conf | 15 |
18 files changed, 153 insertions, 107 deletions
@@ -305,6 +305,7 @@ fi %files addon-kerneloops %defattr(-,root,root,-) +%config(noreplace) %{_sysconfdir}/%{name}/plugins/Kerneloops.conf %config(noreplace) %{_sysconfdir}/%{name}/plugins/KerneloopsScanner.conf %{_bindir}/dumpoops %{_libdir}/%{name}/libKerneloops.so* diff --git a/inc/abrtlib.h b/inc/abrtlib.h index 63836ea..8e18808 100644 --- a/inc/abrtlib.h +++ b/inc/abrtlib.h @@ -199,6 +199,7 @@ char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa); char *encode_base64(const void *src, int length); bool dot_or_dotdot(const char *filename); char *last_char_is(const char *s, int c); +bool string_to_bool(const char *s); /* C++ style stuff */ diff --git a/lib/Plugins/CCpp.cpp b/lib/Plugins/CCpp.cpp index 8362216..8a86e14 100644 --- a/lib/Plugins/CCpp.cpp +++ b/lib/Plugins/CCpp.cpp @@ -948,6 +948,8 @@ void CAnalyzerCCpp::DeInit() void CAnalyzerCCpp::SetSettings(const map_plugin_settings_t& pSettings) { + m_pSettings = pSettings; + map_plugin_settings_t::const_iterator end = pSettings.end(); map_plugin_settings_t::const_iterator it; it = pSettings.find("MemoryMap"); @@ -976,7 +978,7 @@ void CAnalyzerCCpp::SetSettings(const map_plugin_settings_t& pSettings) map_plugin_settings_t CAnalyzerCCpp::GetSettings() { - map_plugin_settings_t ret; + map_plugin_settings_t ret = m_pSettings; ret["MemoryMap"] = m_bMemoryMap ? "yes" : "no"; ret["DebugInfo"] = m_sDebugInfo; diff --git a/lib/Plugins/CCpp.h b/lib/Plugins/CCpp.h index f3f911e..a37ba67 100644 --- a/lib/Plugins/CCpp.h +++ b/lib/Plugins/CCpp.h @@ -35,6 +35,8 @@ class CAnalyzerCCpp : public CAnalyzer unsigned m_nDebugInfoCacheMB; std::string m_sOldCorePattern; std::string m_sDebugInfo; + map_plugin_settings_t m_pSettings; + public: CAnalyzerCCpp(); virtual std::string GetLocalUUID(const std::string& pDebugDumpDir); diff --git a/lib/Plugins/Kerneloops.conf b/lib/Plugins/Kerneloops.conf new file mode 100644 index 0000000..47b242f --- /dev/null +++ b/lib/Plugins/Kerneloops.conf @@ -0,0 +1,2 @@ +# compatibility with kerneloops.org tool +InformAllUsers = yes diff --git a/lib/Plugins/Kerneloops.cpp b/lib/Plugins/Kerneloops.cpp index e01bb42..e072ada 100644 --- a/lib/Plugins/Kerneloops.cpp +++ b/lib/Plugins/Kerneloops.cpp @@ -63,6 +63,16 @@ std::string CAnalyzerKerneloops::GetGlobalUUID(const std::string& pDebugDumpDir) return GetLocalUUID(pDebugDumpDir); } +void CAnalyzerKerneloops::SetSettings(const map_plugin_settings_t& pSettings) +{ + m_pSettings = pSettings; +} + +map_plugin_settings_t CAnalyzerKerneloops::GetSettings() +{ + return m_pSettings; +} + PLUGIN_INFO(ANALYZER, CAnalyzerKerneloops, "Kerneloops", diff --git a/lib/Plugins/Kerneloops.h b/lib/Plugins/Kerneloops.h index edd00d8..c22e787 100644 --- a/lib/Plugins/Kerneloops.h +++ b/lib/Plugins/Kerneloops.h @@ -33,10 +33,15 @@ class CAnalyzerKerneloops : public CAnalyzer { + private: + map_plugin_settings_t m_pSettings; + public: virtual std::string GetLocalUUID(const std::string& pDebugDumpDir); virtual std::string GetGlobalUUID(const std::string& pDebugDumpDir); virtual void CreateReport(const std::string& pDebugDumpDir, int force) {} + virtual void SetSettings(const map_plugin_settings_t& pSettings); + virtual map_plugin_settings_t GetSettings(); }; #endif diff --git a/lib/Plugins/KerneloopsReporter.cpp b/lib/Plugins/KerneloopsReporter.cpp index c57a1ef..712adf0 100644 --- a/lib/Plugins/KerneloopsReporter.cpp +++ b/lib/Plugins/KerneloopsReporter.cpp @@ -98,12 +98,10 @@ std::string CKerneloopsReporter::Report(const map_crash_report_t& pCrashReport, const std::string& pArgs) { int ret = -1; - map_crash_report_t::const_iterator it; update_client(_("Creating and submitting a report...")); - it = pCrashReport.begin(); - it = pCrashReport.find(FILENAME_KERNELOOPS); + map_crash_report_t::const_iterator it = pCrashReport.find(FILENAME_KERNELOOPS); if (it != pCrashReport.end()) { ret = http_post_to_kerneloops_site( m_sSubmitURL.c_str(), @@ -111,19 +109,18 @@ std::string CKerneloopsReporter::Report(const map_crash_report_t& pCrashReport, ); } - if (ret) - { + if (ret) { /* FIXME: be more informative */ - throw CABRTException(EXCEP_PLUGIN, std::string("CKerneloopsReporter::Report(): Report has not been sent...")); + throw CABRTException(EXCEP_PLUGIN, std::string("CKerneloopsReporter::Report(): Report has not been sent...")); } return "Kernel oops report was uploaded to: " + m_sSubmitURL; } void CKerneloopsReporter::SetSettings(const map_plugin_settings_t& pSettings) { - if (pSettings.find("SubmitURL") != pSettings.end()) - { - m_sSubmitURL = pSettings.find("SubmitURL")->second; + map_plugin_settings_t::const_iterator it = pSettings.find("SubmitURL"); + if (it != pSettings.end()) { + m_sSubmitURL = it->second; } } diff --git a/lib/Plugins/KerneloopsScanner.cpp b/lib/Plugins/KerneloopsScanner.cpp index 9e1bd53..c7756ba 100644 --- a/lib/Plugins/KerneloopsScanner.cpp +++ b/lib/Plugins/KerneloopsScanner.cpp @@ -43,7 +43,6 @@ CKerneloopsScanner::CKerneloopsScanner() { int cnt_FoundOopses; - m_sSysLogFile = "/var/log/messages"; /* Scan dmesg, on first call only */ cnt_FoundOopses = ScanDmesg(); @@ -54,16 +53,21 @@ CKerneloopsScanner::CKerneloopsScanner() void CKerneloopsScanner::Run(const std::string& pActionDir, const std::string& pArgs) { - int cnt_FoundOopses; + const char *syslog_file = "/var/log/messages"; + map_plugin_settings_t::const_iterator it = m_pSettings.find("SysLogFile"); + if (it != m_pSettings.end()) + { + syslog_file = it->second.c_str(); + } - cnt_FoundOopses = ScanSysLogFile(m_sSysLogFile.c_str()); + int cnt_FoundOopses = ScanSysLogFile(syslog_file); if (cnt_FoundOopses > 0) { SaveOopsToDebugDump(); /* * This marker in syslog file prevents us from * re-parsing old oopses (any oops before it is * ignored by ScanSysLogFile()). The only problem - * is that we can't be sure here that m_sSysLogFile + * is that we can't be sure here that syslog_file * is the file where syslog(xxx) stuff ends up. */ openlog("abrt", 0, LOG_KERN); @@ -97,8 +101,7 @@ void CKerneloopsScanner::SaveOopsToDebugDump() try { CDebugDump debugDump; - // UID of kerneloops is -1 - debugDump.Create(path, -1); + debugDump.Create(path, 0); debugDump.SaveText(FILENAME_ANALYZER, "Kerneloops"); debugDump.SaveText(FILENAME_EXECUTABLE, "kernel"); debugDump.SaveText(FILENAME_KERNEL, first_line); @@ -180,20 +183,12 @@ int CKerneloopsScanner::ScanSysLogFile(const char *filename) void CKerneloopsScanner::SetSettings(const map_plugin_settings_t& pSettings) { - map_plugin_settings_t::const_iterator it = pSettings.find("SysLogFile"); - if (it != pSettings.end()) - { - m_sSysLogFile = it->second; - } + m_pSettings = pSettings; } map_plugin_settings_t CKerneloopsScanner::GetSettings() { - map_plugin_settings_t ret; - - ret["SysLogFile"] = m_sSysLogFile; - - return ret; + return m_pSettings; } PLUGIN_INFO(ACTION, diff --git a/lib/Plugins/KerneloopsScanner.h b/lib/Plugins/KerneloopsScanner.h index 6ac0461..7352393 100644 --- a/lib/Plugins/KerneloopsScanner.h +++ b/lib/Plugins/KerneloopsScanner.h @@ -34,7 +34,7 @@ class CKerneloopsScanner : public CAction { private: - std::string m_sSysLogFile; + map_plugin_settings_t m_pSettings; /* For "dumpoops" tool */ public: diff --git a/lib/Plugins/Makefile.am b/lib/Plugins/Makefile.am index 428c2d6..0502f5e 100644 --- a/lib/Plugins/Makefile.am +++ b/lib/Plugins/Makefile.am @@ -1,30 +1,42 @@ AM_CPPFLAGS = -I$(srcdir)/../../inc -I$(srcdir)/../Utils pluginslibdir=$(PLUGINS_LIB_DIR) -pluginslib_LTLIBRARIES = libCCpp.la \ - libMailx.la \ - libSQLite3.la \ - libLogger.la \ - libKerneloopsScanner.la\ - libKerneloopsReporter.la\ - libKerneloops.la \ - libRunApp.la \ - libSOSreport.la \ - libBugzilla.la \ - libCatcut.la \ - libTicketUploader.la \ - libPython.la \ - libFileTransfer.la - -dist_pluginslib_DATA = KerneloopsReporter.GTKBuilder Logger.GTKBuilder\ - Mailx.GTKBuilder Bugzilla.GTKBuilder\ - TicketUploader.GTKBuilder Catcut.GTKBuilder - -pluginsconfdir=$(PLUGINS_CONF_DIR) -dist_pluginsconf_DATA = CCpp.conf Mailx.conf SQLite3.conf Logger.conf KerneloopsScanner.conf KerneloopsReporter.conf Bugzilla.conf Catcut.conf TicketUploader.conf FileTransfer.conf - - -man_MANS = abrt-FileTransfer.7 abrt-Bugzilla.7 abrt-KerneloopsReporter.7\ - abrt-KerneloopsScanner.7 abrt-Logger.7 abrt-Mailx.7 abrt-plugins.7\ +pluginslib_LTLIBRARIES = \ + libCCpp.la \ + libMailx.la \ + libSQLite3.la \ + libLogger.la \ + libKerneloopsScanner.la\ + libKerneloopsReporter.la\ + libKerneloops.la \ + libRunApp.la \ + libSOSreport.la \ + libBugzilla.la \ + libCatcut.la \ + libTicketUploader.la \ + libPython.la \ + libFileTransfer.la + +dist_pluginslib_DATA = \ + KerneloopsReporter.GTKBuilder Logger.GTKBuilder \ + Mailx.GTKBuilder Bugzilla.GTKBuilder \ + TicketUploader.GTKBuilder Catcut.GTKBuilder + +pluginsconfdir = $(PLUGINS_CONF_DIR) +dist_pluginsconf_DATA = \ + CCpp.conf \ + Mailx.conf \ + SQLite3.conf \ + Logger.conf \ + Kerneloops.conf \ + KerneloopsScanner.conf \ + KerneloopsReporter.conf \ + Bugzilla.conf \ + Catcut.conf \ + TicketUploader.conf \ + FileTransfer.conf + +man_MANS = abrt-FileTransfer.7 abrt-Bugzilla.7 abrt-KerneloopsReporter.7 \ + abrt-KerneloopsScanner.7 abrt-Logger.7 abrt-Mailx.7 abrt-plugins.7 \ abrt-SQLite3.7 abrt-RunApp.7 abrt-TicketUploader.7 # + abrt-Catcut.7 diff --git a/lib/Utils/xfuncs.cpp b/lib/Utils/xfuncs.cpp index 533fbfa..97c2f76 100644 --- a/lib/Utils/xfuncs.cpp +++ b/lib/Utils/xfuncs.cpp @@ -355,3 +355,16 @@ std::string concat_path_file(const char *path, const char *filename) lc = last_char_is(path, '/'); return ssprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename); } + +bool string_to_bool(const char *s) +{ + if (s[0] == '1' && s[1] == '\0') + return true; + if (strcasecmp(s, "on") == 0) + return true; + if (strcasecmp(s, "yes") == 0) + return true; + if (strcasecmp(s, "true") == 0) + return true; + return false; +} diff --git a/src/Applet/Applet.cpp b/src/Applet/Applet.cpp index 327e24f..2eed556 100644 --- a/src/Applet/Applet.cpp +++ b/src/Applet/Applet.cpp @@ -49,13 +49,12 @@ static void Crash(DBusMessage* signal) dbus_message_iter_init(signal, &in_iter); const char* progname; r = load_val(&in_iter, progname); - if (r != ABRT_DBUS_MORE_FIELDS) + /* Optional 2nd param: uid */ + const char* uid_str = NULL; + if (r == ABRT_DBUS_MORE_FIELDS) { - error_msg("dbus signal %s: parameter type mismatch", __func__); - return; + r = load_val(&in_iter, uid_str); } - const char* uid_str; - r = load_val(&in_iter, uid_str); if (r != ABRT_DBUS_LAST_FIELD) { error_msg("dbus signal %s: parameter type mismatch", __func__); @@ -66,13 +65,17 @@ static void Crash(DBusMessage* signal) // return; // uid_t uid_num = atol(uid_str); - char* endptr; - int64_t uid_num = strtoll(uid_str,&endptr, 10); - - if ((uid_num != getuid()) && (uid_num != -1)) + if (uid_str != NULL) { - return; + char *end; + errno = 0; + unsigned long uid_num = strtoul(uid_str, &end, 10); + if (errno || *end != '\0' || uid_num != getuid()) + { + return; + } } + const char* message = _("A crash in package %s has been detected"); //applet->AddEvent(uid, progname); applet->SetIconTooltip(message, progname); diff --git a/src/Daemon/CommLayerServer.h b/src/Daemon/CommLayerServer.h index 6ede581..21c1b30 100644 --- a/src/Daemon/CommLayerServer.h +++ b/src/Daemon/CommLayerServer.h @@ -13,7 +13,7 @@ class CCommLayerServer { virtual ~CCommLayerServer(); /* just stubs to be called when not implemented in specific comm layer */ - virtual void Crash(const std::string& progname, const std::string& uid) {} + virtual void Crash(const char *progname, const char *uid_str) {} virtual void JobDone(const char* pDest, const char* pUUID) = 0; virtual void QuotaExceed(const char* str) {} diff --git a/src/Daemon/CommLayerServerDBus.cpp b/src/Daemon/CommLayerServerDBus.cpp index 2483942..e53e087 100644 --- a/src/Daemon/CommLayerServerDBus.cpp +++ b/src/Daemon/CommLayerServerDBus.cpp @@ -46,16 +46,24 @@ static void send_flush_and_unref(DBusMessage* msg) } /* Notify the clients (UI) about a new crash */ -void CCommLayerServerDBus::Crash(const std::string& progname, const std::string& uid) +void CCommLayerServerDBus::Crash(const char *progname, const char *uid_str) { DBusMessage* msg = new_signal_msg("Crash"); - const char* c_progname = progname.c_str(); - const char* c_uid = uid.c_str(); - dbus_message_append_args(msg, - DBUS_TYPE_STRING, &c_progname, - DBUS_TYPE_STRING, &c_uid, - DBUS_TYPE_INVALID); - VERB2 log("Sending signal Crash('%s','%s')", c_progname, c_uid); + if (uid_str) + { + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &progname, + DBUS_TYPE_STRING, &uid_str, + DBUS_TYPE_INVALID); + VERB2 log("Sending signal Crash('%s','%s')", progname, uid_str); + } + else + { + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &progname, + DBUS_TYPE_INVALID); + VERB2 log("Sending signal Crash('%s')", progname); + } send_flush_and_unref(msg); } diff --git a/src/Daemon/CommLayerServerDBus.h b/src/Daemon/CommLayerServerDBus.h index 7b2e31d..f159c73 100644 --- a/src/Daemon/CommLayerServerDBus.h +++ b/src/Daemon/CommLayerServerDBus.h @@ -11,7 +11,7 @@ class CCommLayerServerDBus virtual ~CCommLayerServerDBus(); /* DBus signal senders */ - virtual void Crash(const std::string& progname, const std::string& uid); + virtual void Crash(const char *progname, const char *uid_str); virtual void JobDone(const char* pDest, const char* pUUID); virtual void QuotaExceed(const char* str); diff --git a/src/Daemon/Daemon.cpp b/src/Daemon/Daemon.cpp index 175efe9..fd7951f 100644 --- a/src/Daemon/Daemon.cpp +++ b/src/Daemon/Daemon.cpp @@ -165,6 +165,18 @@ static double GetDirSize(const std::string &pPath, std::string *worst_dir = NULL return size; } +static bool analyzer_has_InformAllUsers(const char *analyzer_name) +{ + CAnalyzer* analyzer = g_pPluginManager->GetAnalyzer(analyzer_name); + if (!analyzer) + return false; + map_plugin_settings_t settings = analyzer->GetSettings(); + map_plugin_settings_t::const_iterator it = settings.find("InformAllUsers"); + if (it == settings.end()) + return false; + return string_to_bool(it->second.c_str()); +} + static void cron_delete_callback_data_cb(gpointer data) { cron_callback_data_t* cronDeleteCallbackData = static_cast<cron_callback_data_t*>(data); @@ -369,8 +381,7 @@ static void FindNewDumps(const char* pPath) map_crash_info_t crashinfo; try { - mw_result_t res; - res = SaveDebugDump(*itt, crashinfo); + mw_result_t res = SaveDebugDump(*itt, crashinfo); switch (res) { case MW_OK: @@ -522,38 +533,23 @@ static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition condition, gpoin map_crash_info_t crashinfo; try { - mw_result_t res; - res = SaveDebugDump(std::string(DEBUG_DUMPS_DIR) + "/" + name, crashinfo); + mw_result_t res = SaveDebugDump(std::string(DEBUG_DUMPS_DIR) + "/" + name, crashinfo); switch (res) { case MW_OK: - log("New crash, saving..."); + log("New crash, saving"); RunActionsAndReporters(crashinfo[CD_MWDDD][CD_CONTENT]); - /* Send dbus signal */ - if(crashinfo[CD_MWANALYZER][CD_CONTENT] == "Kerneloops") - { - // When Kerneloops comes it will be sent uid with -1 - // Applet will detected and show normal user - g_pCommLayer->Crash(crashinfo[CD_PACKAGE][CD_CONTENT], to_string("-1")); - } - else - { - g_pCommLayer->Crash(crashinfo[CD_PACKAGE][CD_CONTENT], crashinfo[CD_UID][CD_CONTENT]); - } - break; + /* Fall through to "send dbus signal" */ case MW_REPORTED: case MW_OCCURED: - log("Already saved crash, deleting..."); + if (res != MW_OK) + log("Already saved crash, just sending dbus signal"); /* Send dbus signal */ - if(crashinfo[CD_MWANALYZER][CD_CONTENT] == "Kerneloops") - { - // When Kerneloops comes it will be sent uid with -1 - // Applet will detected and show normal user - g_pCommLayer->Crash(crashinfo[CD_PACKAGE][CD_CONTENT], to_string("-1")); - } - else { - g_pCommLayer->Crash(crashinfo[CD_PACKAGE][CD_CONTENT], crashinfo[CD_UID][CD_CONTENT]); + const char *uid_str = analyzer_has_InformAllUsers(crashinfo[CD_MWANALYZER][CD_CONTENT].c_str()) + ? NULL + : crashinfo[CD_UID][CD_CONTENT].c_str(); + g_pCommLayer->Crash(crashinfo[CD_PACKAGE][CD_CONTENT].c_str(), uid_str); } //DeleteDebugDumpDir(std::string(DEBUG_DUMPS_DIR) + "/" + name); break; @@ -564,7 +560,7 @@ static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition condition, gpoin case MW_IN_DB: case MW_FILE_ERROR: default: - log("Corrupted or bad crash, deleting..."); + log("Corrupted or bad crash, deleting"); DeleteDebugDumpDir(std::string(DEBUG_DUMPS_DIR) + "/" + name); break; } diff --git a/src/Daemon/abrt.conf b/src/Daemon/abrt.conf index a02cef4..e9845e5 100644 --- a/src/Daemon/abrt.conf +++ b/src/Daemon/abrt.conf @@ -1,32 +1,31 @@ # test conf file. it will be generated in the future -# common abrt settings +# Common abrt settings [ Common ] # With this option set to "yes", # only crashes in signed packages will be analyzed. OpenGPGCheck = no # GPG keys OpenGPGPublicKeys = /etc/pki/rpm-gpg/RPM-GPG-KEY-fedora -# blacklisted packages +# Blacklisted packages BlackList = nspluginwrapper -# enabled plugins -# there has to be exactly one database plugin +# Enabled plugins. There has to be exactly one database plugin EnabledPlugins = SQLite3, CCpp, Logger, Kerneloops, KerneloopsScanner, KerneloopsReporter, Bugzilla, Python # Database Database = SQLite3 -# max size for crash storage [MiB] +# Max size for crash storage [MiB] MaxCrashReportsSize = 1000 -# vector of actions and reporters which are activated immediately after a crash occurs +# Vector of actions and reporters which are activated immediately after a crash occurs # ActionsAndReporters = Mailx("[abrt] new crash was detected") -# reporters association with analyzers +# Reporters association with analyzers [ AnalyzerActionsAndReporters ] Kerneloops = KerneloopsReporter CCpp = Bugzilla, Logger Python = Bugzilla, Logger # CCpp : xorg-x11-apps = RunApp("date", "RunApp") -# repeated calling of Action plugins +# Repeated calling of Action plugins [ Cron ] # h:m - at h:m an action plugin is activated # s - every s seconds is an action plugin activated |
