From 81d9c9e574866cd5e10794875872c9c08b11bd2b Mon Sep 17 00:00:00 2001 From: ystarnaud Date: Wed, 19 Nov 2014 18:33:11 +0100 Subject: [PATCH] Events framework Added the ability to have users run commands, reboot the system or quit sgminer based on system events. These events can be defined and placed throughout the sgminer source. So far the events are: "gpu_sick", "gpu_dead" and "idle". I will document further shortly. Config example available here: http://pastebin.com/2rRv3EzH --- Makefile.am | 1 + config_parser.c | 2 +- doc/configuration.md | 148 +++++++++++++++++ events.c | 270 +++++++++++++++++++++++++++++++ events.h | 23 +++ sgminer.c | 36 +++++ winbuild/sgminer.vcxproj | 2 + winbuild/sgminer.vcxproj.filters | 6 + 8 files changed, 487 insertions(+), 1 deletion(-) create mode 100644 events.c create mode 100644 events.h diff --git a/Makefile.am b/Makefile.am index d6210fdc..a91fd19c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -43,6 +43,7 @@ sgminer_SOURCES += adl.c adl.h adl_functions.h sgminer_SOURCES += pool.c pool.h sgminer_SOURCES += algorithm.c algorithm.h sgminer_SOURCES += config_parser.c config_parser.h +sgminer_SOURCES += events.c events.h sgminer_SOURCES += ocl/patch_kernel.c ocl/patch_kernel.h sgminer_SOURCES += ocl/build_kernel.c ocl/build_kernel.h sgminer_SOURCES += ocl/binary_kernel.c ocl/binary_kernel.h diff --git a/config_parser.c b/config_parser.c index 3f5430a2..018e1bd9 100644 --- a/config_parser.c +++ b/config_parser.c @@ -662,7 +662,7 @@ static char *parse_config_array(json_t *obj, char *parentkey, bool fileconf) json_t *val; //fix parent key - remove extra "s" to match opt names (e.g. --pool-gpu-memclock not --pools-gpu-memclock) - if(!strcasecmp(parentkey, "pools") || !strcasecmp(parentkey, "profiles")) + if(!strcasecmp(parentkey, "pools") || !strcasecmp(parentkey, "profiles") || !strcasecmp(parentkey, "events")) parentkey[(strlen(parentkey) - 1)] = '\0'; json_array_foreach(obj, idx, val) diff --git a/doc/configuration.md b/doc/configuration.md index 856987b2..2d4d99be 100644 --- a/doc/configuration.md +++ b/doc/configuration.md @@ -8,8 +8,11 @@ * [Globals and the Default Profile](#globals-and-the-default-profile) * [Working with Profiles and Pool Specific Settings](#working-with-profiles-and-pool-specific-settings) * [Include and Includes](#include-and-includes) +* [Events](#events) * [CLI Only options](#cli-only-options) * [Config-file and CLI options](#config-file-and-cli-options) +* [Event options](#event-options) +* [Event Types](#event-types) --- @@ -233,6 +236,34 @@ There is no limit as to how includes can be used as long as they follow proper j --- +## Events + +Users can now execute commands or perform certain tasks when pre-defined events occur while mining. + +For example, one might want their miner to email them via a script when the miner goes idle and reboot the computer when a GPU goes dead. This gives users a little more flexibility controlling their mining uptime without necessarily resorting to external watchdog programs that, in some cases, can be troublesome. + +Here is a configuration example of the above scenario: +``` +... +"events":[ + { + "on":"idle", + "runcmd":"/bin/mailscript \"Miner Idle\" \"Hey! My miner went idle!\"" + }, + { + "on":"gpu_dead", + "reboot":"yes" + } +], +... +``` + +For more details on configuration options, see [Event Options](#event-options) below. + +[Top](#configuration-and-command-line-options) + +--- + ## CLI Only options * [config](#config) `--config` or `-c` @@ -2446,3 +2477,120 @@ Displays extra work time debug information. *Default:* `false` [Top](#configuration-and-command-line-options) :: [Config-file and CLI options](#config-file-and-cli-options) :: [Miscellaneous Options](#miscellaneous-options) + +--- + +## Event options + +* [on](#on) +* [runcmd](#runcmd) +* [reboot](#reboot) +* [reboot-delay](#reboot-delay) +* [quit](#quit) +* [quit-message](#quit-message) + +### on + +Specify which event type to respond on. See below for a list of supported [event types](#event-types) + +*Available*: Events + +*Config File Syntax:* `"on":""` + +*Command Line Syntax:* `--event-on ` + +*Argument:* `string` Name of the event type + +*Default:* None + +[Top](#configuration-and-command-line-options) :: [Event options](#event-options) + +### runcmd + +Specify a command to run when the event occurs. Please remember to properly escape quotes (") with backslashes (\\) if you need to specify multi-word parameters enclosed in quotes (") for your commands: `\"` + +*Available*: Events + +*Config File Syntax:* `"runcmd":""` + +*Command Line Syntax:* `--event-runcmd ` + +*Argument:* `string` Command to execute on event + +*Default:* None + +[Top](#configuration-and-command-line-options) :: [Event options](#event-options) + +### reboot + +Reboot when event occurs. + +*Available*: Events + +*Config File Syntax:* `"reboot":""` + +*Command Line Syntax:* `--event-reboot ` + +*Argument:* `string` Yes: `"true"` `"yes"` `"1"` or No: `"false"` `"no"` `"0"` + +*Default:* `false` + +[Top](#configuration-and-command-line-options) :: [Event options](#event-options) + +### reboot-delay + +Wait a number of seconds before rebooting when event occurs. This is useful if you also want to fire off a script via `runcmd` prior to rebooting, giving it extra seconds to finish. + +*Available*: Events + +*Config File Syntax:* `"reboot-delay":""` + +*Command Line Syntax:* `--event-reboot-delay ` + +*Argument:* `number` Seconds to wait before reboot + +*Default:* `0` + +[Top](#configuration-and-command-line-options) :: [Event options](#event-options) + +### quit + +Exit sgminer when event occurs. + +*Available*: Events + +*Config File Syntax:* `"quit":""` + +*Command Line Syntax:* `--event-quit ` + +*Argument:* `string` Yes: `"true"` `"yes"` `"1"` or No: `"false"` `"no"` `"0"` + +*Default:* `false` + +[Top](#configuration-and-command-line-options) :: [Event options](#event-options) + +### quit-message + +Message to display on sgminer exit when event occurs. + +*Available*: Events + +*Config File Syntax:* `"quit-message":""` + +*Command Line Syntax:* `--event-quit-message ""` + +*Argument:* `string` Message + +*Default:* `event_type` + +[Top](#configuration-and-command-line-options) :: [Event options](#event-options) + +--- + +## Event Types + +* `idle` Occurs when a GPU goes idle for not performing any work or when no work has been received in 10 minutes. +* `gpu_sick` Occurs when a GPU fails to respond for 2 minutes +* `gpu_dead` Occurs when a GPU fails to respond for 10 minutes + +[Top](#configuration-and-command-line-options) \ No newline at end of file diff --git a/events.c b/events.c new file mode 100644 index 00000000..9f9db1a9 --- /dev/null +++ b/events.c @@ -0,0 +1,270 @@ +/* + * Copyright 2013-2014 sgminer developers (see AUTHORS.md) + * Copyright 2011-2013 Con Kolivas + * Copyright 2011-2012 Luke Dashjr + * Copyright 2010 Jeff Garzik + * + * 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 3 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "compat.h" +#include "miner.h" +#include "events.h" +#include "config_parser.h" + +// global event list +event_t *events = NULL, *last_event = NULL; + +/*************************************************** + * Helper functions + **************************************************/ +static void *cmd_thread(void *cmdp) +{ + const char *cmd = (const char*)cmdp; + + applog(LOG_DEBUG, "Executing command: %s", cmd); + system(cmd); + + return NULL; +} + +static void runcmd(const char *cmd) +{ + if (empty_string(cmd)) + return; + + pthread_t pth; + pthread_create(&pth, NULL, cmd_thread, (void*)cmd); +} + +/**************************************************** +* Event list functions +****************************************************/ +//find an event by event_type +static event_t *get_event(const char *event_type) +{ + event_t *p = events; + + while (p != NULL) + { + if(!strcasecmp(p->event_type, event_type)) + return p; + + p = p->next; + } + + return NULL; +} + +// add event to the list +static event_t *add_event(unsigned int id) +{ + event_t *event; + + // allocate memory + if (!(event = (event_t *)malloc(sizeof(event_t)))) + quit(1, "malloc() failed in add_event()"); + + // set defaults + event->id = id; + event->event_type = ""; + event->runcmd = ""; + event->reboot = false; + event->reboot_delay = 0; + event->quit = false; + event->quit_msg = ""; + event->prev = event->next = NULL; + + // first event? + if(events == NULL) + { + events = event; + last_event = event; + } + // no, append to the list + else + { + last_event->next = event; + event->prev = last_event; + last_event = event; + } + + return event; +} + +// remove event from the list +static void remove_event(event_t *event) +{ + // only event? + if(event == events && event == last_event) + events = last_event = NULL; + // first event? + else if(event == events) + { + event->next->prev = NULL; + events = event->next; + } + // last event? + else if(event == last_event) + { + event->prev->next = NULL; + last_event = event->prev; + } + // in the middle + else + { + event->prev->next = event->next; + event->next->prev = event->prev; + } + + // free memory + free(event); +} + +#ifndef EVENT_ADD_CHECK + #define EVENT_ADD_CHECK if (!last_event || (last_event->id != json_array_index)) add_event(json_array_index); +#endif + +/******************************************** +* Config functions +*********************************************/ +char *set_event_type(const char *event_type) +{ + event_t *event; + + // make sure event type doesn't already exist + if ((event = get_event(event_type)) != NULL) + return NULL; + + EVENT_ADD_CHECK; + + last_event->event_type = event_type; + + return NULL; +} + +char *set_event_runcmd(const char *cmd) +{ + EVENT_ADD_CHECK; + + last_event->runcmd = cmd; + + return NULL; +} + +char *set_event_reboot(const char *arg) +{ + EVENT_ADD_CHECK; + + if (empty_string(arg)) + return NULL; + + last_event->reboot = strtobool(arg); + + applog(LOG_NOTICE, "Event %s reboot = %s", last_event->event_type, ((last_event->reboot)?"true":"false")); + + return NULL; +} + +char *set_event_reboot_delay(const char *delay) +{ + EVENT_ADD_CHECK; + + last_event->reboot_delay = atoi(delay); + + // if the reboot delay is greater than 0 seconds, automatically turn on reboot + if (last_event->reboot_delay > 0) + last_event->reboot = true; + + return NULL; +} + +char *set_event_quit(const char *arg) +{ + EVENT_ADD_CHECK; + + if (empty_string(arg)) + return NULL; + + last_event->quit = strtobool(arg); + + applog(LOG_DEBUG, "Event %s quit = %s", last_event->event_type, ((last_event->quit)?"true":"false")); + + return NULL; +} + +char *set_event_quit_message(const char *msg) +{ + EVENT_ADD_CHECK; + + last_event->quit_msg = msg; + + // if the quit message is set, automatically turn on quit + if (!empty_string(last_event->quit_msg)) + last_event->quit = true; + + return NULL; +} + +/****************************************** +* Event functions +*******************************************/ +void event_notify(const char *event_type) +{ + event_t *event; + + // find an event of the specified type + if ((event = get_event(event_type)) == NULL) + return; + + applog(LOG_DEBUG, "Executing event %s", event_type); + + // run command if defined + if (!empty_string(event->runcmd)) + runcmd(event->runcmd); + + // reboot if set + if (event->reboot == true) + { + //wait specified amount of time + if (event->reboot_delay > 0) + { + applog(LOG_NOTICE, "waiting %d to reboot", event->reboot_delay); + sleep(event->reboot_delay); + } + + #ifdef WIN32 + runcmd("shutdown /r /t 0"); + #else + applog(LOG_NOTICE, "running shutdown -r now"); + runcmd("/sbin/shutdown -r now"); + #endif + } + + // quit sgminer if set + if (event->quit == true) + quit(0, ((empty_string(event->quit_msg))?event_type:event->quit_msg)); + +} \ No newline at end of file diff --git a/events.h b/events.h new file mode 100644 index 00000000..b2b884e2 --- /dev/null +++ b/events.h @@ -0,0 +1,23 @@ +#ifndef EVENTS_H +#define EVENTS_H + +typedef struct event { + unsigned int id; + const char *event_type; + const char *runcmd; + bool reboot; + unsigned int reboot_delay; + bool quit; + const char *quit_msg; + struct event *prev, *next; +} event_t; + +extern char *set_event_type(const char *event_type); +extern char *set_event_runcmd(const char *cmd); +extern char *set_event_reboot(const char *arg); +extern char *set_event_reboot_delay(const char *delay); +extern char *set_event_quit(const char *arg); +extern char *set_event_quit_message(const char *msg); +extern void event_notify(const char *event_type); + +#endif /* EVENTS_H */ \ No newline at end of file diff --git a/sgminer.c b/sgminer.c index 0458ce33..089b43f6 100644 --- a/sgminer.c +++ b/sgminer.c @@ -59,6 +59,7 @@ char *curly = ":D"; #include "algorithm.h" #include "pool.h" #include "config_parser.h" +#include "events.h" #if defined(unix) || defined(__APPLE__) #include @@ -1378,6 +1379,27 @@ struct opt_table opt_config_table[] = { OPT_WITH_ARG("--expiry|-E", set_int_0_to_9999, opt_show_intval, &opt_expiry, "Upper bound on how many seconds after getting work we consider a share from it stale"), + + // event options + OPT_WITH_ARG("--event-on", + set_event_type, NULL, NULL, + "Select event type to perform task on"), + OPT_WITH_ARG("--event-runcmd", + set_event_runcmd, NULL, NULL, + "Command to perform on event"), + OPT_WITH_ARG("--event-reboot", + set_event_reboot, NULL, NULL, + "Reboot the system on event"), + OPT_WITH_ARG("--event-reboot-delay", + set_event_reboot_delay, NULL, NULL, + "Delay in seconds to wait before rebooting"), + OPT_WITH_ARG("--event-quit", + set_event_quit, NULL, NULL, + "Quit sgminer on event"), + OPT_WITH_ARG("--event-quit-message", + set_event_quit_message, NULL, NULL, + "Quit message when quitting sgminer on event"), + OPT_WITHOUT_ARG("--failover-only", opt_set_bool, &opt_fail_only, "Don't leak work to backup pools when primary pool is lagging"), @@ -1776,6 +1798,10 @@ struct opt_table opt_config_table[] = { opt_set_bool, NULL, NULL, opt_hidden), OPT_WITH_ARG("--profiles", opt_set_bool, NULL, NULL, opt_hidden), + OPT_WITH_ARG("--includes", + opt_set_bool, NULL, NULL, opt_hidden), + OPT_WITH_ARG("--events", + opt_set_bool, NULL, NULL, opt_hidden), OPT_WITH_ARG("--difficulty-multiplier", set_difficulty_multiplier, NULL, NULL, "(deprecated) Difficulty multiplier for jobs received from stratum pools"), @@ -5838,6 +5864,7 @@ static struct work *hash_pop(bool blocking) if (rc && !no_work) { no_work = true; applog(LOG_WARNING, "Waiting for work to be available from pools."); + event_notify("idle"); } } while (!HASH_COUNT(staged_work)); } @@ -7019,6 +7046,7 @@ static void hash_sole_work(struct thr_info *mythr) applog(LOG_ERR, "%s %d failure, disabling!", drv->name, cgpu->device_id); cgpu->deven = DEV_DISABLED; dev_error(cgpu, REASON_THREAD_ZERO_HASH); + event_notify("idle"); cgpu->shutdown = true; break; } @@ -7516,6 +7544,11 @@ static void *watchdog_thread(void __maybe_unused *userdata) cgtime(&now); + // check last getwork time if greater than 10 mins, declare idle... + if ((time(NULL) - last_getwork) >= 600) { + event_notify("idle"); + } + if (!sched_paused && !should_run()) { applog(LOG_WARNING, "Pausing execution as per stop time %02d:%02d scheduled", schedstop.tm.tm_hour, schedstop.tm.tm_min); @@ -7594,6 +7627,8 @@ static void *watchdog_thread(void __maybe_unused *userdata) cgtime(&thr->sick); dev_error(cgpu, REASON_DEV_SICK_IDLE_60); + event_notify("gpu_sick"); + #ifdef HAVE_ADL if (adl_active && cgpu->has_adl && gpu_activity(gpu) > 50) { applog(LOG_ERR, "GPU still showing activity suggesting a hard hang."); @@ -7610,6 +7645,7 @@ static void *watchdog_thread(void __maybe_unused *userdata) cgtime(&thr->sick); dev_error(cgpu, REASON_DEV_DEAD_IDLE_600); + event_notify("gpu_dead"); } else if (now.tv_sec - thr->sick.tv_sec > 60 && (cgpu->status == LIFE_SICK || cgpu->status == LIFE_DEAD)) { /* Attempt to restart a GPU that's sick or dead once every minute */ diff --git a/winbuild/sgminer.vcxproj b/winbuild/sgminer.vcxproj index 0728c416..abf80519 100644 --- a/winbuild/sgminer.vcxproj +++ b/winbuild/sgminer.vcxproj @@ -275,6 +275,7 @@ + @@ -335,6 +336,7 @@ + diff --git a/winbuild/sgminer.vcxproj.filters b/winbuild/sgminer.vcxproj.filters index 0ba37eab..3487079d 100644 --- a/winbuild/sgminer.vcxproj.filters +++ b/winbuild/sgminer.vcxproj.filters @@ -200,6 +200,9 @@ Source Files\algorithm + + Source Files + @@ -379,6 +382,9 @@ Header Files\algorithm + + Header Files +