diff --git a/clients/lcdexec/lcdexec.c b/clients/lcdexec/lcdexec.c index 208310f..63145b9 100644 --- a/clients/lcdexec/lcdexec.c +++ b/clients/lcdexec/lcdexec.c @@ -6,23 +6,13 @@ * COPYING file distributed with this package. * * Copyright (c) 2002, Joris Robijn - * - */ - -/* - * How to use submenus in the config file: - * [lcdexec] - * MenuCommand="Shut down,shutdown -h now" - * SubMenu="Internet,inet" - * # The inet submenu is filled as follows: - * inet_MenuCommand="open,inet open" - * inet_MenuCommand="close,inet close" - * + * Copyright (c) 2006, Peter Marschall */ #include #include #include +#include #include "getopt.h" @@ -62,15 +52,15 @@ extern int optind, optopt, opterr; /* Variables set by config */ #define UNSET_INT -1 #define UNSET_STR "\01" -char * configfile = UNSET_STR; -char * address = UNSET_STR; +char * configfile = NULL; +char * address = NULL; int port = UNSET_INT; int foreground_mode = UNSET_INT; -char * appname = UNSET_STR; static int report_level = UNSET_INT; static int report_dest = UNSET_INT; +char *displayname = NULL; -Menu * main_menu; +MenuEntry *main_menu; /* Other variables */ int sock = -1; @@ -78,11 +68,10 @@ int sock = -1; /* Function prototypes */ int process_command_line(int argc, char **argv); int process_configfile(char * configfile); -int split(char * str, char delim, char * parts[], int maxparts); -int connect_and_setup (); +int connect_and_setup(); int process_response(char * str); -int exec_command(char * command); -int main_loop (); +int exec_command(MenuEntry *cmd); +int main_loop(); #define CHAIN(e,f) { if (e>=0) { e=(f); }} @@ -191,14 +180,16 @@ int process_command_line(int argc, char **argv) int process_configfile(char * configfile) { - if (strcmp(configfile, UNSET_STR) == 0) { + char * tmp; + + if (configfile == NULL) configfile = DEFAULT_CONFIGFILE; - } + if (config_read_file(configfile) < 0) { report(RPT_WARNING, "Could not read config file: %s", configfile); } - if (strcmp(address, UNSET_STR) == 0) { + if (address == NULL) { address = strdup(config_get_string(progname, "Address", 0, "localhost")); } if (port == UNSET_INT) { @@ -216,17 +207,24 @@ int process_configfile(char * configfile) foreground_mode = config_get_bool(progname, "Foreground", 0, 0); } - main_menu = menu_read("", progname); + if ((tmp = config_get_string(progname, "DisplayName", 0, NULL)) != NULL) + displayname = strdup(tmp); + + main_menu = menu_read(NULL, "MainMenu"); +#if defined(DEBUG) + menu_dump(main_menu); +#endif + + // fail on non-existent main menu; + if (main_menu == NULL) { + report(RPT_ERR, "no main menu found in configuration"); + return -1; + } - if (main_menu->num_menucmds == 0 && main_menu->num_submenus == 0) { - main_menu->menucmd_name[0] = "echo test"; - main_menu->menucmd_exec[0] = "echo \"This is a test using the echo command.\""; - main_menu->num_menucmds ++; - } return 0; } -int connect_and_setup () +int connect_and_setup() { report(RPT_INFO, "Connecting to %s:%d", address, port); @@ -235,20 +233,31 @@ int connect_and_setup () return -1; } - /* Create our menu */ + /* init connection and set client name */ sock_send_string(sock, "hello\n"); - sock_printf(sock, "client_set -name \"%s\"\n", progname); + if (displayname != NULL) { + sock_printf(sock, "client_set -name {%s}\n", displayname); + } + else { + struct utsname unamebuf; + + if (uname(&unamebuf) == 0) + sock_printf(sock, "client_set -name {%s %s}\n", progname, unamebuf.nodename); + else + sock_printf(sock, "client_set -name {%s}\n", progname); + } - if (menu_send_to_LCDd(main_menu, "", sock) < 0) { + /* Create our menu */ + if (menu_sock_send(main_menu, NULL, sock) < 0) { return -1; } return 0; } -int process_response(char * str) +int process_response(char *str) { - char *argv[10]; + char *argv[15]; int argc; char *str2 = strdup(str); /* get_args modifies str2 */ @@ -270,7 +279,7 @@ int process_response(char * str) return -1; } if (strcmp(argv[1], "select") == 0) { - char *exec; + MenuEntry *exec; if (argc < 3) { report(RPT_WARNING, "Server gave invalid response"); @@ -279,8 +288,8 @@ int process_response(char * str) } /* Find the id */ - exec = menu_find_cmd_of_id(main_menu, argv[2]); - if (!exec) { + exec = menu_find_by_id(main_menu, atoi(argv[2])); + if (exec == NULL) { report(RPT_WARNING, "Could not find the item id given by the server"); free(str2); return -1; @@ -303,42 +312,46 @@ int process_response(char * str) return 0; } -int exec_command(char * command) +int exec_command(MenuEntry *cmd) { - char *argv[4]; + if ((cmd != NULL) && (menu_command(cmd) != NULL)) { + const char *command = menu_command(cmd); + const char *argv[4]; - report(RPT_NOTICE, "Executing: %s", command); + report(RPT_NOTICE, "Executing: %s", command); - argv[0] = getenv("SHELL"); - argv[1] = "-c"; - argv[2] = command; - argv[3] = NULL; + argv[0] = getenv("SHELL"); + argv[1] = "-c"; + argv[2] = command; + argv[3] = NULL; - if (!argv[0]) { - report(RPT_ERR, "SHELL environment variable not set."); - return -1; + if (!argv[0]) { + report(RPT_ERR, "SHELL environment variable not set."); + return -1; + } + + switch (fork()) { + case 0: + /* We're the child. Execute the command. */ + execv(argv[0], argv); + exit(0); + break; + case -1: + report(RPT_ERR, "Could not fork"); + return -1; + default: + /* We're the parent */ + break; + } + return 0; } - - switch (fork()) { - case 0: - /* We're the child. Execute the command. */ - execv(argv[0], argv); - exit(0); - break; - case -1: - report(RPT_ERR, "Could not fork"); - return -1; - default: - /* We're the parent */ - break; - } - return 0; + return -1; } int main_loop () { int num_bytes; - char buf[80]; + char buf[100]; int w = 0; /* Continuously check if we get a menu event... */ diff --git a/clients/lcdexec/lcdexec.conf b/clients/lcdexec/lcdexec.conf index 75402cf..65b3688 100644 --- a/clients/lcdexec/lcdexec.conf +++ b/clients/lcdexec/lcdexec.conf @@ -17,41 +17,54 @@ ReportToSyslog=false # run in foreground [default: false; legal: true, false] Foreground=false -# menu commands for the main menu -# format: , -MenuCommand="You can say A, echo A" -MenuCommand="Or you can say B, echo B" - -# sub menu for the main menu -# format: , -SubMenu="A menu, menu1" - -# sub menu commands -# option names must start with the name of a defined menu -# format: see menu commands -menu1_MenuCommand="p, echo P" -menu1_MenuCommand="q, echo Q" +# display name for the main menu [default: lcdexec HOST] +#DisplayName=lcdexec + + +# main menu definition +[MainMenu] +# the Entry=... lines (one for each menu entry) tell it is a menu definition +Entry=CmdA +Entry=CmdB +Entry=MenuC + +# definition of a command +[CmdA] +# name to display in the menu instead of the section name +DisplayName="You can say A" +# the exec=... line tells that it is a command +Exec="echo a" + +[CmdB] +DisplayName="Or you can say B" +Exec="echo b" + +# definition of a menu +# a menu contains an Entry=... line for each menu entry +[MenuC] +DisplayName="A menu" +Entry=CmdP +Entry=CmdQ + +[CmdP] +DisplayName=P +Exec="echo P" + +[CmdQ] +DisplayName=Q +Exec="echo Q" -# Ideally lcdexec should be re-written so that menu definitions -# look somewhat like: -# -#[Menu] -#Type=Menu -#Entry=SubMenu1 -#Entry=SubMenu2 -# -#[SubMenu1] -#Type=Action -#Exec=/bin/program to execute with parameters -# -#[SubMenu2] -#Type=Action -#Exec=/bin/otherprogram with parameters -# # Further Extensions: +# - shell selectable in command sections +# - type definitions instead implicit depending on Exec/Entry +# (the latter only as fallback) # - use input for parameters e.g. IP-Adresses, Sliders -# - display result if a command on Screen +# e.g. Exec="ifconfig ${IF} ${IP} netmask ${MASK} broadcast ${BCAST}" +# where +# - ${IF} is the result of a selection input screen +# - ${IP}, ${MASK}n ${BCAST} are the results of IP input screens +# - display result if a command on the display # - jump to other menus depending on the output/result of a command # EOF diff --git a/clients/lcdexec/menu.c b/clients/lcdexec/menu.c index ac4dfc1..2793a13 100644 --- a/clients/lcdexec/menu.c +++ b/clients/lcdexec/menu.c @@ -1,12 +1,12 @@ /* - * lcdexec.c + * menu.c * This file is part of lcdexec, an LCDproc client. * * This file is released under the GNU General Public License. Refer to the * COPYING file distributed with this package. * * Copyright (c) 2002, Joris Robijn - * + * Copyright (c) 2006, Peter Marschall */ #include @@ -20,203 +20,231 @@ #include "menu.h" -Menu * menu_find_submenu(Menu * menu, char * submenu_id); - -Menu * menu_read(char * menu_id, char * progname) +/* recursively read the menu hierarchy */ +MenuEntry *menu_read(MenuEntry *parent, char *name) { - char buf[100]; - char * str; - char * parts[3]; - int i; + static int id = 0; - Menu * menu; - - report(RPT_DEBUG, "Reading menu: [%s]", menu_id); - - menu = malloc(sizeof(Menu)); - menu->num_menucmds = 0; - menu->num_submenus = 0; - - /* Read the commands */ - str = ""; - while ((str != NULL) && (menu->num_menucmds < MAX_NUM_MENUCMDS)) { - - snprintf(buf, sizeof(buf)-1, "%s%sMenuCommand", - menu_id, menu_id[0] ? "_" : ""); - buf[sizeof(buf)-1] = 0; - str = config_get_string(progname, buf, menu->num_menucmds, NULL); - if (!str) { - ; /* No more menucommands */ - } else { - char * str2 = strdup(str); - if (split(str2, ',', parts, 2) == 2) { - menu->menucmd_name[menu->num_menucmds] = parts[0]; - while (parts[1][0] == ' ') parts[1]++; /* Skip spaces */ - menu->menucmd_exec[menu->num_menucmds] = parts[1]; - report(RPT_DEBUG, "Found command: [%s] [%s]", parts[0], parts[1]); - } else { - report(RPT_ERR, "Cannot read MenuCommand: \"%s\"", str); - return NULL; - } - menu->num_menucmds++; - } - } - - /* Read the submenus */ - str = ""; - while (str && menu->num_submenus < MAX_NUM_SUBMENUS) { - - snprintf(buf, sizeof(buf)-1, "%s%sSubmenu", - menu_id, menu_id[0]?"_":""); - buf[sizeof(buf)-1] = 0; - str = config_get_string(progname, buf, menu->num_submenus, NULL); - if (!str) { - ; /* No more submenus */ - } else { - char * str2 = strdup(str); - if (split(str2, ',', parts, 2) == 2) { - menu->submenu_name[menu->num_submenus] = parts[0]; - while (parts[1][0] == ' ') parts[1]++; /* Skip spaces */ - menu->submenu_id[menu->num_submenus] = parts[1]; - report(RPT_DEBUG, "Found submenu: [%s] [%s]", parts[0], parts[1]); - } else { - report(RPT_ERR, "Cannot read Submenu: \"%s\"", str); - return NULL; - } - menu->num_submenus++; - } - } - - /* And do the same for all submenus */ - for (i = 0; i < menu->num_submenus; i++) { - menu->submenu[i] = menu_read (menu->submenu_id[i], progname); - } - - return menu; -} - -int menu_send_to_LCDd(Menu * menu, char * id, int sock) -{ - int i; - char buf[100]; - - for (i = 0; i < menu->num_menucmds; i++) { - snprintf(buf, sizeof(buf)-1, - "menu_add_item \"%s\" \"%s%s%d\" action \"%s\"\n", - id, id, id[0]?"_":"", i, menu->menucmd_name[i]); - buf[sizeof(buf)-1] = '\0'; - if (sock_send_string(sock, buf) < 0) - return -1; - snprintf(buf, sizeof(buf)-1, - "menu_set_item \"%s\" \"%s%s%d\" -menu_result quit\n", - id, id, id[0]?"_":"", i); - buf[sizeof(buf)-1] = '\0'; - if (sock_send_string(sock, buf) < 0) - return -1; - } - for (i = 0; i < menu->num_submenus; i++) { - snprintf(buf, sizeof(buf)-1, - "menu_add_item \"%s\" \"%s\" menu \"%s\"\n", - id, menu->submenu_id[i], menu->submenu_name[i]); - buf[sizeof(buf)-1] = '\0'; - if (sock_send_string(sock, buf) < 0) - return -1; - } - - /* And do the same for all submenus */ - for (i = 0; i < menu->num_submenus; i++) { - if (menu_send_to_LCDd(menu->submenu[i], menu->submenu_id[i], sock) < 0) - return -1; - } - return 0; -} - -char * menu_find_cmd_of_id(Menu * menu, char * id) -{ - char * p; - char * submenu_id; - char * item_id; - int i; - - /* Find the submenu_id part of the id */ - submenu_id = strdup(id); - p = strrchr(submenu_id, '_'); - - /* Do we need to search the submenus ? */ - if (p) { - /* There is a menu id prepended to the id */ - *p = '\0'; /* Crop the submenu_id string */ - item_id = p + 1; - menu = menu_find_submenu(menu, submenu_id); - if (!menu) { - report(RPT_WARNING, "Server reported an unknown id: %s", id); - free(submenu_id); + if (config_has_section(name)) { + MenuEntry *me = calloc(1, sizeof(MenuEntry)); + + if (me == NULL) return NULL; + // set common entries + me->id = id++; + me->name = strdup(name); + if (me->name == NULL) { + //menu_free(me); + return NULL; + } + + me->displayname = strdup(config_get_string(name, "DisplayName", 0, name)); + if (me->name == NULL) { + //menu_free(me); + return NULL; + } + + me->entries = NULL; + me->next = NULL; + me->command = NULL; + + if (config_get_string(name, "Entry", 0, NULL) != NULL) { + MenuEntry **addr = &me->entries; + char *entryname; + int index = 0; + + // it is a sub-menu + me->type = menu; + + while ((entryname = config_get_string(name, "Entry", index++, NULL)) != NULL) { + MenuEntry *entry = menu_read(me, entryname); + + if (entry == NULL) { + //menu_free(me); + return NULL; + } + + *addr = entry; + addr = &entry->next; + } + } + else if (config_get_string(name, "Exec", 0, NULL) != NULL) { + // it's a command to execute + me->type = exec; + + me->command = strdup(config_get_string(name, "Exec", 0, "")); + if (me->command == NULL) { + //menu_free(me); + return NULL; + } } - } - else { - item_id = id; - } + else { + //menu_free(me); + return NULL; + } - /* Determine the number at the end of the id */ - i = strtol(item_id, &p, 10); - if (*item_id != '\0' && *p == '\0' - && i >= 0 && i < menu->num_menucmds) { - /* OK */ - free(submenu_id); - return menu->menucmd_exec[i]; - } else { - report(RPT_WARNING, "Server reported an unknown id: %s", id); - free(submenu_id); - return NULL; - } - -} - -Menu * menu_find_submenu(Menu * menu, char * submenu_id) -{ - int i; - Menu * found; - - /* Search for the submenu in this menu and all submenus */ - for (i = 0; i < menu->num_submenus; i++) { - if (strcmp(menu->submenu_id[i], submenu_id) == 0) - return menu->submenu[i]; - found = menu_find_submenu(menu->submenu[i], submenu_id); - if (found) - return found; - } + return me; + } return NULL; } -int split(char * str, char delim, char * parts[], int maxparts) -/* Splits a string into parts, to which pointers will be returned in &parts. - * The return value is the number of parts. - * maxparts is the maximum number of parts returned. If more parts exist - * they are (unsplit) in the last part. - * The parts are split at the character delim. - * No new space will be allocated, the string str will be mutated ! - */ + +/* create LCDproc commands for the menu entry hierarchy and send it to the server */ +int menu_sock_send(MenuEntry *me, MenuEntry *parent, int sock) { - char * p1 = str; - char * p2; - int part_nr = 0; + if ((me != NULL) && (sock > 0)) { + char parent_id[12]; - /* Find the delim char to end the current part */ - while ((part_nr < maxparts - 1) && ((p2 = strchr(p1, delim)) != NULL)) { + if (parent == NULL) + strcpy(parent_id, ""); + else + sprintf(parent_id, "%d", parent->id); - /* subsequent parts... */ - *p2 = '\0'; - parts[part_nr] = p1; + switch (me->type) { + MenuEntry *entry; - p1 = p2 + 1; /* Just after the delim char */ - part_nr++; - } - /* and the last part... */ - parts[part_nr] = p1; - part_nr++; - - return part_nr; + case menu: + // don't create a separate entry fro the main menu + if (parent != NULL) { + if (sock_printf(sock, "menu_add_item \"%s\" \"%d\" menu \"%s\"\n", + parent_id, me->id, me->displayname) < 0) + return -1; + } + + // recursively do it for the menu's sub-menus + for (entry = me->entries; entry != NULL; entry = entry->next) { + if (menu_sock_send(entry, (parent != NULL) ? me : NULL, sock) < 0) + return -1; + } + break; + case exec: + if (sock_printf(sock, "menu_add_item \"%s\" \"%d\" action \"%s\"\n", + parent_id, me->id, me->displayname) < 0) + return -1; + if (sock_printf(sock, "menu_set_item \"%s\" \"%d\" -menu_result quit\n", + parent_id, me->id) < 0) + return -1; + break; + case unknown: + default: + return -1; + } + return 0; + } + return -1; } + +/* find menu entry by its id */ +MenuEntry *menu_find_by_id(MenuEntry *me, int id) +{ + if (me != NULL) { + if (me->id == id) + return me; + + if (me->type == menu) { + MenuEntry *entry; + + for (entry = me->entries; entry != NULL; entry = entry->next) { + MenuEntry *result = menu_find_by_id(entry, id); + + if (result != NULL) + return result; + } + } + } + return NULL; +} + + +/* return command of a menu entry */ +const char *menu_command(MenuEntry *me) +{ + if ((me != NULL) && (me->type == exec)) + return me->command; + + return NULL; +} + + +/* free menu entry hierarchy */ +void menu_free(MenuEntry *me) +{ + if (me != NULL) { + switch (me->type) { + MenuEntry *entry; + + case menu: + for (entry = me->entries; entry != NULL; ) { + MenuEntry *old = entry; + + entry = entry->next; + old->next = NULL; + menu_free(old); + } + me->entries = NULL; + break; + case exec: + if (me->command != NULL) + free(me->command); + me->command = NULL; + break; + case unknown: + default: + break; + } + + if (me->name != NULL) + free(me->name); + me->name = NULL; + + if (me->displayname != NULL) + free(me->displayname); + me->displayname = NULL; + + me->type = unknown; + + free(me); + } +} + + +#if defined(DEBUG) +/* dump menu entry hierarchy to screen */ +void menu_dump(MenuEntry *me) +{ + if (me != NULL) { + report(RPT_DEBUG, "[%s]", me->name); + if (me->displayname != NULL) + report(RPT_DEBUG, "DisplayName=\"%s\"", me->displayname); + + switch (me->type) { + MenuEntry *entry; + + case menu: + for (entry = me->entries; entry != NULL; entry = entry->next) + report(RPT_DEBUG, "Entry=%s", entry->name); + break; + case exec: + report(RPT_DEBUG, "Exec=\"%s\"", me->command); + break; + case unknown: + default: + report(RPT_DEBUG, "ERROR: unknown menu entry type"); + break; + } + report(RPT_DEBUG, ""); + + // recursively walk through sub-menus + if (me->type == menu) { + MenuEntry *entry; + + for (entry = me->entries; entry != NULL; entry = entry->next) + menu_dump(entry); + } + } +} +#endif + +/* EOF */ diff --git a/clients/lcdexec/menu.h b/clients/lcdexec/menu.h index 211faf6..04ad20b 100644 --- a/clients/lcdexec/menu.h +++ b/clients/lcdexec/menu.h @@ -18,21 +18,32 @@ #include "shared/configfile.h" #include "shared/sockets.h" -#define MAX_NUM_MENUCMDS 40 -#define MAX_NUM_SUBMENUS 20 -typedef struct Menu { - char * menucmd_name[MAX_NUM_MENUCMDS]; - char * menucmd_exec[MAX_NUM_MENUCMDS]; - int num_menucmds; - char * submenu_name[MAX_NUM_SUBMENUS]; - char * submenu_id[MAX_NUM_SUBMENUS]; - struct Menu * submenu[MAX_NUM_SUBMENUS]; - int num_submenus; -} Menu; +typedef enum { + unknown = 0, + menu = 1, + exec = 2, +} MenuType; -Menu * menu_read (char * menu_id, char * progname); -int menu_send_to_LCDd( Menu * menu, char * id, int sock ); -char * menu_find_cmd_of_id( Menu * menu, char * id ); -int split( char * str, char delim, char * parts[], int maxparts ); +typedef struct menu_entry { + char *name; + char *displayname; + int id; + MenuType type; + + // variables necessary for type menu + struct menu_entry *entries; + struct menu_entry *next; + + // variables necessary for type exec + char *command; +} MenuEntry; + + +MenuEntry *menu_read(MenuEntry *parent, char *name); +int menu_sock_send(MenuEntry *me, MenuEntry *parent, int sock); +MenuEntry *menu_find_by_id(MenuEntry *me, int id); +const char *menu_command(MenuEntry *me); +void menu_free(MenuEntry *me); +