]> git.tuebingen.mpg.de Git - paraslash.git/blobdiff - audiod_command.c
server: Convert com_addatt() to lopsub.
[paraslash.git] / audiod_command.c
index b3312940412a93511c4c730485e3db30a80bc9c9..8a87d91e108f4470f127d35538bb16ef920f5c76 100644 (file)
@@ -1,21 +1,27 @@
 /*
- * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file audiod_command.c commands for para_audiod */
+/** \file audiod_command.c Commands for para_audiod. */
 
+#include <netinet/in.h>
+#include <sys/socket.h>
 #include <regex.h>
 #include <sys/types.h>
-#include <dirent.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
+#include <lopsub.h>
 
 #include "para.h"
+#include "audiod_cmd.lsg.h"
 #include "audiod.cmdline.h"
 #include "list.h"
-#include "close_on_fork.h"
 #include "sched.h"
 #include "ggo.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "grab_client.h"
 #include "error.h"
 #include "net.h"
 #include "daemon.h"
 #include "string.h"
+#include "write.h"
 #include "fd.h"
-#include "audiod_command_list.h"
+#include "version.h"
 
+extern struct sched sched;
 extern char *stat_item_values[NUM_STAT_ITEMS];
 
+/** The maximal number of simultaneous connections. */
+#define MAX_STAT_CLIENTS 50
 
-/** iterate over the array of all audiod commands */
-#define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++)
+/** Pointer to a command handler function. */
+typedef int (*audiod_cmd_handler_t)(int, struct lls_parse_result *);
 
-static int client_write(int fd, const char *buf)
+/** The lopsub user_data pointer. Only the command handler at the moment. */
+struct audiod_command_info {
+       audiod_cmd_handler_t handler; /**< Implementation of the command. */
+};
+
+/** Define the user_data pointer as expected by lopsub. */
+#define EXPORT_AUDIOD_CMD_HANDLER(_cmd) \
+       /** Implementation of _cmd. */ \
+       const struct audiod_command_info lsg_audiod_cmd_com_ ## _cmd ## _user_data = { \
+               .handler = com_ ## _cmd \
+       };
+
+/** Flags used for the stat command of para_audiod. */
+enum stat_client_flags {
+       /** Enable parser-friendly output. */
+       SCF_PARSER_FRIENDLY = 1,
+};
+
+/**
+ * Describes a status client of para_audiod.
+ *
+ * There's one such structure per audiod client that sent the 'stat' command.
+ *
+ * A status client is identified by its file descriptor.  para_audiod
+ * keeps a list of connected status clients.
+ */
+struct stat_client {
+       /** The stat client's file descriptor. */
+       int fd;
+       /** Bitmask of those status items the client is interested in. */
+       uint64_t item_mask;
+       /** See \ref stat_client flags. */
+       unsigned flags;
+       /** Its entry in the list of stat clients. */
+       struct list_head node;
+};
+
+static INITIALIZED_LIST_HEAD(client_list);
+static int num_clients;
+
+/** The list of all status items used by para_{server,audiod,gui}. */
+const char *status_item_list[] = {STATUS_ITEM_ARRAY};
+
+static void dump_stat_client_list(void)
 {
-       size_t len;
+       struct stat_client *sc;
 
-       if (!buf)
-               return 0;
-       len = strlen(buf);
-       return write(fd, buf, len) != len? -E_CLIENT_WRITE: 1;
+       list_for_each_entry(sc, &client_list, node)
+               PARA_INFO_LOG("stat client on fd %d\n", sc->fd);
 }
+/**
+ * Add a status client to the list.
+ *
+ * \param fd The file descriptor of the client.
+ * \param mask Bitfield of status items for this client.
+ * \param parser_friendly Enable parser-friendly output mode.
+ *
+ * Only those status items having the bit set in \a mask will be
+ * sent to the client.
+ *
+ * \return Positive value on success, or -E_TOO_MANY_CLIENTS if
+ * the number of connected clients exceeds #MAX_STAT_CLIENTS.
+ */
+static int stat_client_add(int fd, uint64_t mask, int parser_friendly)
+{
+       struct stat_client *new_client;
+       int ret;
 
-__malloc static char *audiod_status_string(void)
+       if (num_clients >= MAX_STAT_CLIENTS) {
+               PARA_ERROR_LOG("maximal number of stat clients (%d) exceeded\n",
+                       MAX_STAT_CLIENTS);
+               return -E_TOO_MANY_CLIENTS;
+       }
+       ret = dup(fd);
+       if (ret < 0)
+               return -ERRNO_TO_PARA_ERROR(errno);
+       new_client = para_calloc(sizeof(*new_client));
+       new_client->fd = ret;
+       PARA_INFO_LOG("adding client on fd %d\n", new_client->fd);
+       new_client->item_mask = mask;
+       if (parser_friendly)
+               new_client->flags = SCF_PARSER_FRIENDLY;
+       para_list_add(&new_client->node, &client_list);
+       dump_stat_client_list();
+       num_clients++;
+       return 1;
+}
+
+static void close_stat_client(struct stat_client *sc)
 {
-       const char *status = (audiod_status == AUDIOD_ON)?
-               "on" : (audiod_status == AUDIOD_OFF)? "off": "sb";
-       return para_strdup(status);
+       PARA_INFO_LOG("closing client fd %d\n", sc->fd);
+       close(sc->fd);
+       list_del(&sc->node);
+       free(sc);
+       num_clients--;
 }
 
-static int get_play_time_slot_num(void)
+/**
+ * Empty the status clients list.
+ *
+ * This iterates over the list of connected status clients, closes each client
+ * file descriptor and frees the resources.
+ */
+void close_stat_clients(void)
 {
-       int i, oldest = -1;
+       struct stat_client *sc, *tmp;
 
-       FOR_EACH_SLOT(i) {
-               struct slot_info *s = &slot[i];
-               if (!s->wng)
+       list_for_each_entry_safe(sc, tmp, &client_list, node)
+               close_stat_client(sc);
+       assert(num_clients == 0);
+}
+
+/**
+ * Write a message to all connected status clients.
+ *
+ * \param item_num The number of the status item of \a msg.
+ *
+ * On write errors, remove the status client from the client list and close its
+ * file descriptor.
+ */
+void stat_client_write_item(int item_num)
+{
+       struct stat_client *sc, *tmp;
+       struct para_buffer pb = {.flags = 0};
+       struct para_buffer pfpb = {.flags = PBF_SIZE_PREFIX};
+       const uint64_t one = 1;
+       char *msg = stat_item_values[item_num];
+       struct para_buffer *b;
+
+       list_for_each_entry_safe(sc, tmp, &client_list, node) {
+               int ret;
+
+               if (!((one << item_num) & sc->item_mask))
                        continue;
-               if (oldest >= 0 && tv_diff(&s->wstime, &slot[oldest].wstime,
-                               NULL) > 0)
+               b = (sc->flags & SCF_PARSER_FRIENDLY)? &pfpb : &pb;
+               if (!b->buf)
+                       WRITE_STATUS_ITEM(b, item_num, "%s\n", msg? msg : "");
+               ret = write(sc->fd, b->buf, b->offset);
+               if (ret == b->offset)
                        continue;
-               oldest = i;
+               /* write error or short write */
+               close_stat_client(sc);
+               dump_stat_client_list();
        }
-       return oldest;
+       free(pb.buf);
+       free(pfpb.buf);
 }
 
-__malloc static char *decoder_flags(void)
+/**
+ * Check if string is a known status item.
+ *
+ * \param item Buffer containing the text to check.
+ *
+ * \return If \a item is a valid status item, the number of that status item is
+ * returned. Otherwise, this function returns \p -E_UNKNOWN_STAT_ITEM.
+ */
+static int stat_item_valid(const char *item)
 {
        int i;
-       char flags[MAX_STREAM_SLOTS + 1];
-
-       FOR_EACH_SLOT(i) {
-               struct slot_info *s = &slot[i];
-               char flag = '0';
-               if (s->receiver_node)
-                       flag += 1;
-               if (s->fc)
-                       flag += 2;
-               if (s->wng)
-                       flag += 4;
-               flags[i] = flag;
+       if (!item || !*item) {
+               PARA_ERROR_LOG("%s\n", "no item");
+               return -E_UNKNOWN_STAT_ITEM;
        }
-       flags[MAX_STREAM_SLOTS] = '\0';
-       return para_strdup(flags);
+       FOR_EACH_STATUS_ITEM(i)
+               if (!strcmp(status_item_list[i], item))
+                       return i;
+       PARA_ERROR_LOG("invalid stat item: %s\n", item);
+       return -E_UNKNOWN_STAT_ITEM;
+}
+
+static int client_write(int fd, const char *buf)
+{
+       size_t len;
+
+       if (!buf)
+               return 0;
+       len = strlen(buf);
+       return write(fd, buf, len) != len? -E_CLIENT_WRITE: 1;
+}
+
+__malloc static char *audiod_status_string(void)
+{
+       const char *status = (audiod_status == AUDIOD_ON)?
+               "on" : (audiod_status == AUDIOD_OFF)? "off": "sb";
+       return para_strdup(status);
 }
 
 static int dump_commands(int fd)
@@ -90,10 +234,11 @@ static int dump_commands(int fd)
        char *buf = para_strdup(""), *tmp = NULL;
        int i;
        ssize_t ret;
+       const struct lls_command *cmd;
 
-       FOR_EACH_COMMAND(i) {
-               tmp = make_message("%s%s\t%s\n", buf, audiod_cmds[i].name,
-                       audiod_cmds[i].description);
+       for (i = 1; (cmd = lls_cmd(i, audiod_cmd_suite)); i++) {
+               tmp = make_message("%s%s\t%s\n", buf, lls_command_name(cmd),
+                       lls_purpose(cmd));
                free(buf);
                buf = tmp;
        }
@@ -102,99 +247,80 @@ static int dump_commands(int fd)
        return ret;
 }
 
-/*
- * command handlers don't close their fd on errors (ret < 0) so that
- * its caller can send an error message. Otherwise (ret >= 0) it's up
- * to each individual command to close the fd if necessary.
- */
-
-int com_help(int fd, int argc, char **argv)
+static int com_help(int fd, struct lls_parse_result *lpr)
 {
-       int i, ret;
-       char *buf;
-       const char *dflt = "No such command. Available commands:\n";
-
-       if (argc < 2) {
-               ret = dump_commands(fd);
-               goto out;
+       int ret;
+       char *buf, *errctx;
+       const struct lls_command *cmd;
+
+       ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+       if (ret < 0) {
+               if (errctx) {
+                       buf = make_message("%s\n", errctx);
+                       client_write(fd, buf);
+                       free(buf);
+                       free(errctx);
+               }
+               return ret;
        }
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(audiod_cmds[i].name, argv[1]))
-                       continue;
-               buf = make_message(
-                       "NAME\n\t%s -- %s\n"
-                       "SYNOPSIS\n\tpara_audioc %s\n"
-                       "DESCRIPTION\n%s\n",
-                       argv[1],
-                       audiod_cmds[i].description,
-                       audiod_cmds[i].usage,
-                       audiod_cmds[i].help
-               );
-               ret = client_write(fd, buf);
+       if (lls_num_inputs(lpr) == 0)
+               return dump_commands(fd);
+       ret = lls(lls_lookup_subcmd(lls_input(0, lpr), audiod_cmd_suite,
+               &errctx));
+       if (ret < 0) {
+               buf = make_message("%s: %s\nAvailable commands:\n", errctx?
+                       errctx : lls_input(0, lpr), para_strerror(-ret));
+               if (client_write(fd, buf) >= 0)
+                       dump_commands(fd);
+               free(errctx);
                free(buf);
                goto out;
        }
-       ret = client_write(fd, dflt);
-       if (ret > 0)
-               ret = dump_commands(fd);
+       cmd = lls_cmd(ret, audiod_cmd_suite);
+       buf = lls_long_help(cmd);
+       assert(buf);
+       ret = client_write(fd, buf);
+       free(buf);
 out:
-       if (ret >= 0)
-               close(fd);
-       return ret;
+       return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(help)
 
-int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_tasks(int fd, __a_unused struct lls_parse_result *lpr)
 {
-       char *tl = get_task_list();
-       int ret = 1;
-       if (tl)
-               ret = client_write(fd, tl);
-       free(tl);
-       if (ret > 0)
-               close(fd);
-       return ret;
-}
+       int ret;
+       char *tl = get_task_list(&sched);
 
-int com_kill(int fd, int argc, char **argv)
-{
-       int i, ret = 1;
-       if (argc < 2)
-               return -E_AUDIOD_SYNTAX;
-       for (i = 1; i < argc; i++) {
-               ret = kill_task(argv[i]);
-               if (ret < 0)
-                       break;
-       }
-       if (ret > 0)
-               close(fd);
+       if (!tl) /* no tasks registered yet */
+               return 0;
+       ret = client_write(fd, tl);
+       free(tl);
        return ret;
 }
+EXPORT_AUDIOD_CMD_HANDLER(tasks)
 
-int com_stat(int fd, int argc, char **argv)
+static int com_stat(int fd, struct lls_parse_result *lpr)
 {
        int i, ret, parser_friendly = 0;
        uint64_t mask = 0;
        const uint64_t one = 1;
        struct para_buffer b = {.flags = 0};
+       const struct lls_opt_result *r;
+       unsigned num_inputs;
 
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strncmp(arg, "-p", 2)) {
-                       parser_friendly = 1;
-                       b.flags = PBF_SIZE_PREFIX;
-                       continue;
-               }
+       ret = mark_fd_nonblocking(fd);
+       if (ret < 0)
+               return ret;
+       r = lls_opt_result(LSG_AUDIOD_CMD_STAT_OPT_PARSER_FRIENDLY, lpr);
+       if (lls_opt_given(r) > 0) {
+               parser_friendly = 1;
+               b.flags = PBF_SIZE_PREFIX;
        }
-       if (i >= argc)
+       num_inputs = lls_num_inputs(lpr);
+       if (num_inputs == 0)
                mask--; /* set all bits */
-       for (; i < argc; i++) {
-               ret = stat_item_valid(argv[i]);
+       for (i = 0; i < num_inputs; i++) {
+               ret = stat_item_valid(lls_input(i, lpr));
                if (ret < 0)
                        return ret;
                mask |= (one << ret);
@@ -210,139 +336,156 @@ int com_stat(int fd, int argc, char **argv)
        if (ret >= 0)
                ret = stat_client_add(fd, mask, parser_friendly);
        free(b.buf);
-       return ret;
+       return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(stat)
 
-int com_grab(int fd, int argc, char **argv)
+static int com_grab(int fd, struct lls_parse_result *lpr)
 {
-       return grab_client_new(fd, argc, argv);
+       int ret = grab_client_new(fd, lpr, &sched);
+       return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(grab)
 
-__noreturn int com_term(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_term(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
-       close(fd);
-       clean_exit(EXIT_SUCCESS, "terminating on user request");
+       return -E_AUDIOD_TERM;
 }
+EXPORT_AUDIOD_CMD_HANDLER(term)
 
-int com_on(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_on(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_ON;
-       close(fd);
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(on)
 
-int com_off(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_off(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_OFF;
-       close(fd);
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(off)
 
-int com_sb(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_sb(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_STANDBY;
-       close(fd);
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(sb)
 
-int com_cycle(int fd, int argc, char **argv)
+static int com_cycle(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        switch (audiod_status) {
-               case  AUDIOD_ON:
-                       return com_sb(fd, argc, argv);
-                       break;
-               case  AUDIOD_OFF:
-                       return com_on(fd, argc, argv);
-                       break;
-               case  AUDIOD_STANDBY:
-                       return com_off(fd, argc, argv);
-                       break;
+               case  AUDIOD_ON: audiod_status = AUDIOD_STANDBY; break;
+               case  AUDIOD_OFF: audiod_status = AUDIOD_ON; break;
+               case  AUDIOD_STANDBY: audiod_status = AUDIOD_OFF; break;
        }
-       close(fd);
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(cycle)
 
-static int check_perms(uid_t uid)
+static int com_version(int fd, struct lls_parse_result *lpr)
 {
-       int i;
-
-       if (!conf.user_allow_given)
-               return 1;
-       for (i = 0; i < conf.user_allow_given; i++)
-               if (uid == conf.user_allow_arg[i])
-                       return 1;
-       return -E_UCRED_PERM;
+       int ret;
+       char *msg;
+       const struct lls_opt_result *r_v;
+
+       r_v = lls_opt_result(LSG_AUDIOD_CMD_VERSION_OPT_VERBOSE, lpr);
+       if (lls_opt_given(r_v))
+               msg = make_message("%s", version_text("audiod"));
+       else
+               msg = make_message("%s\n", version_single_line("audiod"));
+       ret = client_write(fd, msg);
+       free(msg);
+       return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(version)
 
 /**
- * handle arriving connections on the local socket
+ * Handle arriving connections on the local socket.
  *
- * \param accept_fd the fd to call accept() on
+ * \param accept_fd The fd to accept connections on.
+ * \param rfds If \a accept_fd is not set in \a rfds, do nothing.
  *
- * This is called whenever para_audiod's main task detects an incoming
- * connection by the readability of \a accept_fd. This function reads the
- * command sent by the peer, checks the connecting user's permissions by using
- * unix socket credentials (if supported by the OS) and calls the corresponding
- * command handler if permissions are OK.
+ * This is called in each iteration of the select loop. If there is an incoming
+ * connection on \a accept_fd, this function reads the command sent by the peer,
+ * checks the connecting user's permissions by using unix socket credentials
+ * (if supported by the OS) and calls the corresponding command handler if
+ * permissions are OK.
  *
- * \return positive on success, negative on errors
+ * \return Positive on success, negative on errors, zero if there was no
+ * connection to accept.
  *
  * \sa para_accept(), recv_cred_buffer()
  * */
-int handle_connect(int accept_fd)
+int handle_connect(int accept_fd, fd_set *rfds)
 {
-       int i, argc, ret, clifd = -1;
+       int argc, ret, clifd;
        char buf[MAXLINE], **argv = NULL;
        struct sockaddr_un unix_addr;
        uid_t uid;
-
-       ret = para_accept(accept_fd, &unix_addr, sizeof(struct sockaddr_un));
-       if (ret < 0)
-               goto out;
-       clifd = ret;
+       const struct lls_command *cmd;
+       struct lls_parse_result *lpr;
+       char *errctx;
+       const struct audiod_command_info *aci;
+
+       ret = para_accept(accept_fd, rfds, &unix_addr, sizeof(struct sockaddr_un), &clifd);
+       if (ret <= 0)
+               return ret;
        ret = recv_cred_buffer(clifd, buf, sizeof(buf) - 1);
        if (ret < 0)
                goto out;
        uid = ret;
-       PARA_INFO_LOG("connection from user %i, buf: %s\n",  ret, buf);
-       ret = check_perms(uid);
-       if (ret < 0)
+       PARA_INFO_LOG("connection from UID %d, buf: %s\n", ret, buf);
+       ret = -E_UCRED_PERM;
+       if (!uid_is_whitelisted(uid))
                goto out;
        ret = create_argv(buf, "\n", &argv);
-       if (ret < 0)
+       if (ret <= 0)
                goto out;
        argc = ret;
-       //PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc);
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(audiod_cmds[i].name, argv[0]))
-                       continue;
-               ret = audiod_cmds[i].handler(clifd, argc, argv);
+       ret = lls(lls_lookup_subcmd(argv[0], audiod_cmd_suite, &errctx));
+       if (ret < 0)
                goto out;
-       }
-       ret = -E_INVALID_AUDIOD_CMD;
+       cmd = lls_cmd(ret, audiod_cmd_suite);
+       ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       aci = lls_user_data(cmd);
+       ret = aci->handler(clifd, lpr);
+       lls_free_parse_result(lpr, cmd);
 out:
        free_argv(argv);
-       if (clifd > 0 && ret < 0 && ret != -E_CLIENT_WRITE) {
-               char *tmp = make_message("%s\n", para_strerror(-ret));
+       if (ret < 0 && ret != -E_CLIENT_WRITE) {
+               char *tmp;
+               if (errctx) {
+                       tmp = make_message("%s\n", errctx);
+                       free(errctx);
+                       client_write(clifd, tmp);
+                       free(tmp);
+               }
+               tmp = make_message("%s\n", para_strerror(-ret));
                client_write(clifd, tmp);
                free(tmp);
-               close(clifd);
        }
+       close(clifd);
        return ret;
 }
 
 /**
  * Send the current audiod status to all connected stat clients.
+ *
+ * \param force Whether to write unchanged items.
  */
-void audiod_status_dump(void)
+void audiod_status_dump(bool force)
 {
-       int slot_num = get_play_time_slot_num();
        char *old, *new;
 
        old = stat_item_values[SI_PLAY_TIME];
-       new = get_time_string(slot_num);
+       new = get_time_string();
        if (new) {
-               if (!old || strcmp(old, new)) {
+               if (force || !old || strcmp(old, new)) {
                        free(old);
                        stat_item_values[SI_PLAY_TIME] = new;
                        stat_client_write_item(SI_PLAY_TIME);
@@ -350,9 +493,9 @@ void audiod_status_dump(void)
                        free(new);
        }
 
-       new = uptime_str();
+       new = daemon_get_uptime_str(now);
        old = stat_item_values[SI_AUDIOD_UPTIME];
-       if (!old || strcmp(old, new)) {
+       if (force || !old || strcmp(old, new)) {
                free(old);
                stat_item_values[SI_AUDIOD_UPTIME] = new;
                stat_client_write_item(SI_AUDIOD_UPTIME);
@@ -361,7 +504,7 @@ void audiod_status_dump(void)
 
        old = stat_item_values[SI_AUDIOD_STATUS];
        new = audiod_status_string();
-       if (!old || strcmp(old, new)) {
+       if (force || !old || strcmp(old, new)) {
                free(old);
                stat_item_values[SI_AUDIOD_STATUS] = new;
                stat_client_write_item(SI_AUDIOD_STATUS);
@@ -369,8 +512,8 @@ void audiod_status_dump(void)
                free(new);
 
        old = stat_item_values[SI_DECODER_FLAGS];
-       new = decoder_flags();
-       if (!old || strcmp(old, new)) {
+       new = audiod_get_decoder_flags();
+       if (force || !old || strcmp(old, new)) {
                free(old);
                stat_item_values[SI_DECODER_FLAGS] = new;
                stat_client_write_item(SI_DECODER_FLAGS);