Base: LCDproc 0.5.2
[lcdproc-de200c.git] / clients / lcdexec / lcdexec.c
blob954ba9cf8b18addc0bd6df9f336743f20d8fd2f9
1 /*
2 * lcdexec.c
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
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <sys/utsname.h>
17 #include "getopt.h"
19 #include "shared/str.h"
20 #include "shared/report.h"
21 #include "shared/configfile.h"
22 #include "shared/sockets.h"
24 #include "menu.h"
26 #if !defined(SYSCONFDIR)
27 # define SYSCONFDIR "/etc"
28 #endif
30 #define DEFAULT_CONFIGFILE SYSCONFDIR "/lcdexec.conf"
33 char * help_text =
34 "lcdexec - LCDproc client to execute commands from the LCDd menu\n"
35 "\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"
38 "\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 */
52 #define UNSET_INT -1
53 #define UNSET_STR "\01"
54 char * configfile = NULL;
55 char * address = NULL;
56 int port = UNSET_INT;
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;
63 MenuEntry *main_menu;
65 /* Other variables */
66 int sock = -1;
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)
83 int error = 0;
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);
95 CHAIN_END(error);
97 CHAIN(error, connect_and_setup());
98 CHAIN_END(error);
100 if(foreground != TRUE) {
101 if (daemon(1,1) != 0) {
102 report(RPT_ERR, "Error: daemonize failed");
106 main_loop();
108 return 0;
112 static int process_command_line(int argc, char **argv)
114 int c;
115 int error = 0;
117 /* No error output from getopt */
118 opterr = 0;
120 while ((c = getopt(argc, argv, "c:a:p:fr:s:h")) > 0) {
121 char *end;
122 int temp_int;
124 switch(c) {
125 case 'c':
126 configfile = strdup(optarg);
127 break;
128 case 'a':
129 address = strdup(optarg);
130 break;
131 case 'p':
132 temp_int = strtol(optarg, &end, 0);
133 if ((*optarg != '\0') && (*end == '\0') &&
134 (temp_int > 0) && (temp_int <= 0xFFFF)) {
135 port = temp_int;
136 } else {
137 report(RPT_ERR, "Illegal port value %s", optarg);
138 error = -1;
140 break;
141 case 'f':
142 foreground = TRUE;
143 break;
144 case 'r':
145 temp_int = strtol(optarg, &end, 0);
146 if ((*optarg != '\0') && (*end == '\0') && (temp_int >= 0)) {
147 report_level = temp_int;
148 } else {
149 report(RPT_ERR, "Illegal report level value %s", optarg);
150 error = -1;
152 break;
153 case 's':
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);
157 } else {
158 report(RPT_ERR, "Illegal log destination value %s", optarg);
159 error = -1;
161 break;
162 case 'h':
163 fprintf(stderr, "%s", help_text);
164 exit(0);
165 case ':':
166 report(RPT_ERR, "Missing option argument for %c", optopt);
167 error = -1;
168 break;
169 case '?':
170 default:
171 report(RPT_ERR, "Unknown option: %c", optopt);
172 error = -1;
173 break;
176 return error;
180 static int process_configfile(char *configfile)
182 const char *tmp;
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))
202 ? RPT_DEST_SYSLOG
203 : RPT_DEST_STDERR;
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);
215 else {
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");
228 #if defined(DEBUG)
229 menu_dump(main_menu);
230 #endif
232 // fail on non-existent main menu;
233 if (main_menu == NULL) {
234 report(RPT_ERR, "no main menu found in configuration");
235 return -1;
238 return 0;
242 static int connect_and_setup(void)
244 report(RPT_INFO, "Connecting to %s:%d", address, port);
246 sock = sock_connect(address, port);
247 if (sock < 0) {
248 return -1;
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);
256 else {
257 struct utsname unamebuf;
259 if (uname(&unamebuf) == 0)
260 sock_printf(sock, "client_set -name {%s %s}\n", progname, unamebuf.nodename);
261 else
262 sock_printf(sock, "client_set -name {%s}\n", progname);
265 /* Create our menu */
266 if (menu_sock_send(main_menu, NULL, sock) < 0) {
267 return -1;
270 return 0;
274 static int process_response(char *str)
276 char *argv[15];
277 int argc;
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);
284 if (argc < 1) {
285 free(str2);
286 return 0;
289 if (strcmp(argv[0], "menuevent") == 0) {
290 /* Ah, this is what we were waiting for ! */
292 if (argc < 2) {
293 report(RPT_WARNING, "Server gave invalid response");
294 free(str2);
295 return -1;
297 if (strcmp(argv[1], "select") == 0) {
298 MenuEntry *exec;
300 if (argc < 3) {
301 report(RPT_WARNING, "Server gave invalid response");
302 free(str2);
303 return -1;
306 /* Find the id */
307 exec = menu_find_by_id(main_menu, atoi(argv[2]));
308 if (exec == NULL) {
309 report(RPT_WARNING, "Could not find the item id given by the server");
310 free(str2);
311 return -1;
313 /* The id has been found */
314 exec_command(exec);
316 else {
317 ; /* Ignore other menuevents */
320 else if (strcmp(argv[0], "huh?") == 0) {
321 /* Report errors */
322 report(RPT_WARNING, "Server said: \"%s\"", str);
324 else {
325 ; /* Ignore all other responses */
327 free(str2);
328 return 0;
332 static int exec_command(MenuEntry *cmd)
334 if ((cmd != NULL) && (menu_command(cmd) != NULL)) {
335 const char *command = menu_command(cmd);
336 const char *argv[4];
338 report(RPT_NOTICE, "Executing: %s", command);
340 argv[0] = default_shell;
341 argv[1] = "-c";
342 argv[2] = command;
343 argv[3] = NULL;
345 switch (fork()) {
346 case 0:
347 /* We're the child. Execute the command. */
348 execv(argv[0], (char **) argv);
349 exit(0);
350 break;
351 case -1:
352 report(RPT_ERR, "Could not fork");
353 return -1;
354 default:
355 /* We're the parent */
356 break;
358 return 0;
360 return -1;
364 static int main_loop(void)
366 int num_bytes;
367 char buf[100];
368 int w = 0;
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) {
374 usleep(100000);
376 /* Send an empty line every 3 seconds to make sure the server still exists */
377 if (w++ >= 30) {
378 w = 0;
379 if (sock_send_string(sock, "\n") < 0) {
380 break; /* Out of while loop */
384 else {
385 process_response(buf);
389 report(RPT_ERR, "Server disconnected (or connection error)");
390 return 0;