3 * This file is part of lcdexec, an LCDproc client.
5 * This file is released under the GNU General Public License. Refer to the
6 * COPYING file distributed with this package.
8 * Copyright (c) 2002, Joris Robijn
9 * Copyright (c) 2006, Peter Marschall
15 #include <sys/utsname.h>
19 #include "shared/str.h"
20 #include "shared/report.h"
21 #include "shared/configfile.h"
22 #include "shared/sockets.h"
26 #if !defined(SYSCONFDIR)
27 # define SYSCONFDIR "/etc"
30 #define DEFAULT_CONFIGFILE SYSCONFDIR "/lcdexec.conf"
34 "lcdexec - LCDproc client to execute commands from the LCDd menu\n"
36 "Copyright (c) 2002, Joris Robijn, 2006 Peter Marschall.\n"
37 "This program is released under the terms of the GNU General Public License.\n"
39 "Usage: lcdexec [<options>]\n"
40 " where <options> are:\n"
41 " -c <file> Specify configuration file ["DEFAULT_CONFIGFILE
"]\n"
42 " -a <address> DNS name or IP address of the LCDd server [localhost]\n"
43 " -p <port> port of the LCDd server [13666]\n"
44 " -f Run in foreground\n"
45 " -r <level> Set reporting level (0-5) [2: errors and warnings]\n"
46 " -s <0|1> Report to syslog (1) or stderr (0, default)\n"
47 " -h Show this help\n";
49 char *progname
= "lcdexec";
51 /* Variables set by config */
53 #define UNSET_STR "\01"
54 char * configfile
= NULL
;
55 char * address
= NULL
;
57 int foreground
= FALSE
;
58 static int report_level
= UNSET_INT
;
59 static int report_dest
= UNSET_INT
;
60 char *displayname
= NULL
;
61 char *default_shell
= NULL
;
68 /* Function prototypes */
69 static int process_command_line(int argc
, char **argv
);
70 static int process_configfile(char * configfile
);
71 static int connect_and_setup(void);
72 static int process_response(char *str
);
73 static int exec_command(MenuEntry
*cmd
);
74 static int main_loop(void);
77 #define CHAIN(e,f) { if (e>=0) { e=(f); }}
78 #define CHAIN_END(e) { if (e<0) { report(RPT_CRIT,"Critical error, abort"); exit(e); }}
81 int main(int argc
, char **argv
)
85 CHAIN(error
, process_command_line(argc
, argv
));
86 if (configfile
== NULL
)
87 configfile
= DEFAULT_CONFIGFILE
;
88 CHAIN(error
, process_configfile(configfile
));
90 if (report_dest
== UNSET_INT
|| report_level
== UNSET_INT
) {
91 report_dest
= RPT_DEST_STDERR
;
92 report_level
= RPT_ERR
;
94 set_reporting(progname
, report_level
, report_dest
);
97 CHAIN(error
, connect_and_setup());
100 if(foreground
!= TRUE
) {
101 if (daemon(1,1) != 0) {
102 report(RPT_ERR
, "Error: daemonize failed");
112 static int process_command_line(int argc
, char **argv
)
117 /* No error output from getopt */
120 while ((c
= getopt(argc
, argv
, "c:a:p:fr:s:h")) > 0) {
126 configfile
= strdup(optarg
);
129 address
= strdup(optarg
);
132 temp_int
= strtol(optarg
, &end
, 0);
133 if ((*optarg
!= '\0') && (*end
== '\0') &&
134 (temp_int
> 0) && (temp_int
<= 0xFFFF)) {
137 report(RPT_ERR
, "Illegal port value %s", optarg
);
145 temp_int
= strtol(optarg
, &end
, 0);
146 if ((*optarg
!= '\0') && (*end
== '\0') && (temp_int
>= 0)) {
147 report_level
= temp_int
;
149 report(RPT_ERR
, "Illegal report level value %s", optarg
);
154 temp_int
= strtol(optarg
, &end
, 0);
155 if ((*optarg
!= '\0') && (*end
== '\0') && (temp_int
>= 0)) {
156 report_dest
= (temp_int
? RPT_DEST_SYSLOG
: RPT_DEST_STDERR
);
158 report(RPT_ERR
, "Illegal log destination value %s", optarg
);
163 fprintf(stderr
, "%s", help_text
);
166 report(RPT_ERR
, "Missing option argument for %c", optopt
);
171 report(RPT_ERR
, "Unknown option: %c", optopt
);
180 static int process_configfile(char *configfile
)
184 if (configfile
== NULL
)
185 configfile
= DEFAULT_CONFIGFILE
;
187 if (config_read_file(configfile
) < 0) {
188 report(RPT_WARNING
, "Could not read config file: %s", configfile
);
191 if (address
== NULL
) {
192 address
= strdup(config_get_string(progname
, "Address", 0, "localhost"));
194 if (port
== UNSET_INT
) {
195 port
= config_get_int(progname
, "Port", 0, 13666);
197 if (report_level
== UNSET_INT
) {
198 report_level
= config_get_int(progname
, "ReportLevel", 0, RPT_WARNING
);
200 if (report_dest
== UNSET_INT
) {
201 report_dest
= (config_get_bool(progname
, "ReportToSyslog", 0, 0))
205 if (foreground
!= TRUE
) {
206 foreground
= config_get_bool(progname
, "Foreground", 0, FALSE
);
209 if ((tmp
= config_get_string(progname
, "DisplayName", 0, NULL
)) != NULL
)
210 displayname
= strdup(tmp
);
212 /* try to find a shell that understands the -c COMMAND syntax */
213 if ((tmp
= config_get_string(progname
, "Shell", 0, NULL
)) != NULL
)
214 default_shell
= strdup(tmp
);
216 /* 1st fallback: SHELL environment variable */
217 report(RPT_WARNING
, "Shell not set in configuration, falling back to variable SHELL");
218 default_shell
= getenv("SHELL");
220 /* 2nd fallback: /bin/sh */
221 if (default_shell
== NULL
) {
222 report(RPT_WARNING
, "variable SHELL not set, falling back to /bin/sh");
223 default_shell
= "/bin/sh";
227 main_menu
= menu_read(NULL
, "MainMenu");
229 menu_dump(main_menu
);
232 // fail on non-existent main menu;
233 if (main_menu
== NULL
) {
234 report(RPT_ERR
, "no main menu found in configuration");
242 static int connect_and_setup(void)
244 report(RPT_INFO
, "Connecting to %s:%d", address
, port
);
246 sock
= sock_connect(address
, port
);
251 /* init connection and set client name */
252 sock_send_string(sock
, "hello\n");
253 if (displayname
!= NULL
) {
254 sock_printf(sock
, "client_set -name {%s}\n", displayname
);
257 struct utsname unamebuf
;
259 if (uname(&unamebuf
) == 0)
260 sock_printf(sock
, "client_set -name {%s %s}\n", progname
, unamebuf
.nodename
);
262 sock_printf(sock
, "client_set -name {%s}\n", progname
);
265 /* Create our menu */
266 if (menu_sock_send(main_menu
, NULL
, sock
) < 0) {
274 static int process_response(char *str
)
278 char *str2
= strdup(str
); /* get_args modifies str2 */
280 report(RPT_DEBUG
, "Server said: \"%s\"", str
);
282 /* Check what the server just said to us... */
283 argc
= get_args(argv
, str2
, 10);
289 if (strcmp(argv
[0], "menuevent") == 0) {
290 /* Ah, this is what we were waiting for ! */
293 report(RPT_WARNING
, "Server gave invalid response");
297 if (strcmp(argv
[1], "select") == 0) {
301 report(RPT_WARNING
, "Server gave invalid response");
307 exec
= menu_find_by_id(main_menu
, atoi(argv
[2]));
309 report(RPT_WARNING
, "Could not find the item id given by the server");
313 /* The id has been found */
317 ; /* Ignore other menuevents */
320 else if (strcmp(argv
[0], "huh?") == 0) {
322 report(RPT_WARNING
, "Server said: \"%s\"", str
);
325 ; /* Ignore all other responses */
332 static int exec_command(MenuEntry
*cmd
)
334 if ((cmd
!= NULL
) && (menu_command(cmd
) != NULL
)) {
335 const char *command
= menu_command(cmd
);
338 report(RPT_NOTICE
, "Executing: %s", command
);
340 argv
[0] = default_shell
;
347 /* We're the child. Execute the command. */
348 execv(argv
[0], (char **) argv
);
352 report(RPT_ERR
, "Could not fork");
355 /* We're the parent */
364 static int main_loop(void)
370 /* Continuously check if we get a menu event... */
372 while ((num_bytes
= sock_recv_string(sock
, buf
, sizeof(buf
)-1)) >= 0) {
373 if (num_bytes
== 0) {
376 /* Send an empty line every 3 seconds to make sure the server still exists */
379 if (sock_send_string(sock
, "\n") < 0) {
380 break; /* Out of while loop */
385 process_response(buf
);
389 report(RPT_ERR
, "Server disconnected (or connection error)");