[OpenWrt-Devel] [PATCH procd] hotplug: support for interval commands

Felix Fietkau nbd at openwrt.org
Fri May 8 10:40:14 EDT 2015


On 2015-05-07 12:54, Rafał Miłecki wrote:
> This allows executing code with a given interval. As every command, it
> can be triggered by any uevent.
> 
> Intervals may be useful for counting elapsed time since some action. It
> allows e.g. indicating that button has been pressed for some time and
> can be already released.
> 
> Signed-off-by: Rafał Miłecki <zajec5 at gmail.com>
> ---
> Example usage:
> 	[ "if",
> 		[ "and",
> 			[ "eq", "SUBSYSTEM", "button" ],
> 			[ "eq", "BUTTON", "wps" ],
> 			[ "eq", "ACTION", "pressed" ],
> 		],
> 		[ "set-interval", "wpsbutton", "1000", "/etc/rc.button/%BUTTON%" ]
> 	],
> 	[ "if",
> 		[ "and",
> 			[ "eq", "SUBSYSTEM", "button" ],
> 			[ "eq", "BUTTON", "wps" ],
> 			[ "eq", "ACTION", "released" ],
> 		],
> 		[ "clear-interval", "wpsbutton" ]
> 	],
> 
> Result from /etc/rc.button/wps script:
> SUBSYSTEM:button BUTTON:wps ACTION:pressed SEEN:34 ELAPSED:
> SUBSYSTEM:button BUTTON:wps ACTION:interval SEEN: ELAPSED:1
> SUBSYSTEM:button BUTTON:wps ACTION:interval SEEN: ELAPSED:2
> SUBSYSTEM:button BUTTON:wps ACTION:interval SEEN: ELAPSED:3
> SUBSYSTEM:button BUTTON:wps ACTION:released SEEN:3 ELAPSED:
> ---
>  plug/hotplug.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 143 insertions(+)
> 
> diff --git a/plug/hotplug.c b/plug/hotplug.c
> index 1a98e8b..0b183fb 100644
> --- a/plug/hotplug.c
> +++ b/plug/hotplug.c
> @@ -43,7 +43,19 @@ struct cmd_queue {
>  	void (*handler)(struct blob_attr *msg, struct blob_attr *data);
>  };
>  
> +struct cmd_interval {
> +	char *name;
> +	struct list_head list;
> +
> +	struct timespec start;
> +	struct uloop_timeout timeout;
> +
> +	struct blob_attr *msg;
> +	struct blob_attr *data;
> +};
> +
>  static LIST_HEAD(cmd_queue);
> +static LIST_HEAD(cmd_intervals);
I'd suggest using an avl tree for cmd_intervals, so you can use avl_find
for the lookup instead of the open-coded linked list iterate.

>  static struct uloop_process queue_proc;
>  static struct uloop_timeout last_event;
>  static struct blob_buf b;
> @@ -157,6 +169,129 @@ static void handle_exec(struct blob_attr *msg, struct blob_attr *data)
>  	exit(-1);
>  }
>  
> +static void handle_set_interval_timeout(struct uloop_timeout *timeout)
> +{
> +	struct cmd_interval *interval = container_of(timeout, struct cmd_interval, timeout);
> +	struct blob_attr *cur;
> +	char *argv[8];
> +	int rem, fd, pid;
> +	int msecs = 0;
> +	int i = 0;
> +
> +	blobmsg_for_each_attr(cur, interval->data, rem) {
> +		switch (i) {
> +		case 0:
> +			break;
> +		case 1:
> +			msecs = strtol(blobmsg_get_string(cur), NULL, 0);
> +			break;
> +		default:
> +			argv[i - 2] = blobmsg_data(cur);
> +		}
> +		i++;
> +		if (i - 2 == 7)
> +			break;
> +	}
> +
> +	pid = fork();
> +	if (pid < 0) {
> +		perror("fork");
> +	} else if (pid == 0) {
> +		struct timespec now;
> +		char elapsed[6];
> +
> +		if (i - 2 <= 0)
> +			return;
> +
> +		clock_gettime(CLOCK_MONOTONIC, &now);
> +		snprintf(elapsed, sizeof(elapsed), "%ld", now.tv_sec - interval->start.tv_sec);
> +
> +		blobmsg_for_each_attr(cur, interval->msg, rem)
> +			setenv(blobmsg_name(cur), blobmsg_data(cur), 1);
> +		setenv("ACTION", "interval", 1);
> +		setenv("ELAPSED", elapsed, 1);
> +		unsetenv("SEEN");
> +
> +		if (debug < 3) {
> +			fd = open("/dev/null", O_RDWR);
> +			if (fd > -1) {
> +				dup2(fd, STDIN_FILENO);
> +				dup2(fd, STDOUT_FILENO);
> +				dup2(fd, STDERR_FILENO);
> +				if (fd > STDERR_FILENO)
> +					close(fd);
> +			}
> +		}
> +
> +		argv[i - 2] = NULL;
> +		execvp(argv[0], &argv[0]);
> +		exit(-1);
> +	} else {
> +		uloop_timeout_set(&interval->timeout, msecs);
> +	}
> +}
I think this needs protection against hanging scripts. Add a struct
uloop_process to the interval struct to track the pid. Only spawn a new
task if the process has terminated.

> +
> +static void handle_set_interval(struct blob_attr *msg, struct blob_attr *data)
> +{
> +	static struct blobmsg_policy set_interval_policy[2] = {
> +		{ .type = BLOBMSG_TYPE_STRING },
> +		{ .type = BLOBMSG_TYPE_STRING },
> +	};
> +	struct blob_attr *tb[2];
> +	struct cmd_interval *interval;
> +	struct blob_attr *_msg, *_data;
> +	int msecs;
> +
> +	blobmsg_parse_array(set_interval_policy, 2, tb, blobmsg_data(data), blobmsg_data_len(data));
> +	if (!tb[0] || !tb[1])
> +		return;
> +	msecs = strtol(blobmsg_get_string(tb[1]), NULL, 0);
> +
> +	interval = calloc_a(sizeof(struct cmd_interval),
> +		&_msg, blob_pad_len(msg),
> +		&_data, blob_pad_len(data),
> +		NULL);
> +	if (!interval)
> +		return;
> +
> +	interval->name = strdup(blobmsg_get_string(tb[0]));
> +	interval->msg = _msg;
> +	interval->data = _data;
> +	clock_gettime(CLOCK_MONOTONIC, &interval->start);
> +	interval->timeout.cb = handle_set_interval_timeout;
> +
> +	memcpy(interval->msg, msg, blob_pad_len(msg));
> +	memcpy(interval->data, data, blob_pad_len(data));
> +
> +	list_add_tail(&interval->list, &cmd_intervals);
> +
> +	uloop_timeout_set(&interval->timeout, msecs);
> +}
> +
> +static void handle_clear_interval(struct blob_attr *msg, struct blob_attr *data)
> +{
> +	static struct blobmsg_policy clear_interval_policy = {
> +		.type = BLOBMSG_TYPE_STRING,
> +	};
> +	struct blob_attr *tb;
> +	struct cmd_interval *interval;
> +	char *name;
> +
> +	blobmsg_parse_array(&clear_interval_policy, 1, &tb, blobmsg_data(data), blobmsg_data_len(data));
> +	if (!tb)
> +		return;
> +	name = blobmsg_get_string(tb);
> +
> +	list_for_each_entry(interval, &cmd_intervals, list) {
> +		if (!strcmp(interval->name, name)){
> +			uloop_timeout_cancel(&interval->timeout);
> +			list_del(&interval->list);
> +			free(interval);
> +			return;
> +		}
> +	}
> +}
After adding process tracking, you should kill the interval struct only
if the task has exited. If it is still running, mark it as completed (so
no more timers will be issued for it), and free it after the task has
exited.

- Felix
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel


More information about the openwrt-devel mailing list