Added inotify support for reloading Lua scripts automagically.
[monky] / src / llua.c
1 /* Conky, a system monitor, based on torsmo
2  *
3  * Any original torsmo code is licensed under the BSD license
4  *
5  * All code written since the fork of torsmo is licensed under the GPL
6  *
7  * Please see COPYING for details
8  *
9  * Copyright (c) 2009 Toni Spets
10  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
11  *      (see AUTHORS)
12  * All rights reserved.
13  *
14  * This program is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * You should have received a copy of the GNU General Public License
24  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
25  *
26  */
27
28 #include "conky.h"
29 #include "logging.h"
30
31 #ifdef HAVE_SYS_INOTIFY_H
32 #include <sys/inotify.h>
33
34 void llua_append_notify(const char *name);
35 void llua_rm_notifies(void);
36 static int llua_block_notify = 0;
37 #endif /* HAVE_SYS_INOTIFY_H */
38
39 lua_State *lua_L = NULL;
40
41 void llua_init(void)
42 {
43         if(lua_L) return;
44         lua_L = lua_open();
45         luaL_openlibs(lua_L);
46 }
47
48 void llua_load(const char *script)
49 {
50         int error;
51         if(!lua_L) return;
52         error = luaL_dofile(lua_L, script);
53         if (error) {
54                 ERR("llua_load: %s", lua_tostring(lua_L, -1));
55                 lua_pop(lua_L, 1);
56         } else if (!llua_block_notify) {
57                 llua_append_notify(script);
58         }
59 }
60
61 /*
62         llua_do_call does a flexible call to any Lua function
63         string: <function> [par1] [par2...]
64         retc: the number of return values expected
65 */
66 char *llua_do_call(const char *string, int retc)
67 {
68         static char func[64];
69         int argc = 0;
70
71         char *tmp = strdup(string);
72         char *ptr = strtok(tmp, " ");
73
74         /* proceed only if the function name is present */
75         if(!ptr) {
76                 free(tmp);
77                 return NULL;
78         }
79
80         /* call only conky_ prefixed functions */
81         snprintf(func, 64, "conky_%s", ptr);
82
83         /* push the function name to stack */
84         lua_getglobal(lua_L, func);
85
86         /* parse all function parameters from args and push them to the stack */
87         ptr = strtok(NULL, " ");
88         while(ptr) {
89                 lua_pushstring(lua_L, ptr);
90                 ptr = strtok(NULL, " ");
91                 argc++;
92         }
93
94         free(tmp);
95
96         if(lua_pcall(lua_L, argc, retc, 0) != 0) {
97                 ERR("llua_do_call: function %s execution failed: %s", func, lua_tostring(lua_L, -1));
98                 lua_pop(lua_L, -1);
99                 return NULL;
100         }
101
102         return func;
103 }
104
105 /*
106  * same as llua_do_call() except passes everything after func as one arg.
107  */
108 char *llua_do_read_call(const char *function, const char *arg, int retc)
109 {
110         static char func[64];
111         snprintf(func, 64, "conky_%s", function);
112         
113         /* push the function name to stack */
114         lua_getglobal(lua_L, func);
115
116         /* push function parameter to the stack */
117         lua_pushstring(lua_L, arg);
118
119         if (lua_pcall(lua_L, 1, retc, 0) != 0) {
120                 ERR("llua_do_call: function %s execution failed: %s", func, lua_tostring(lua_L, -1));
121                 lua_pop(lua_L, -1);
122                 return NULL;
123         }
124
125         return func;
126 }
127
128 char *llua_getstring(const char *args)
129 {
130         char *func;
131         char *ret = NULL;
132
133         if(!lua_L) return NULL;
134
135         func = llua_do_call(args, 1);
136         if(func) {
137                 if(!lua_isstring(lua_L, -1)) {
138                         ERR("llua_getstring: function %s didn't return a string, result discarded", func);
139                 } else {
140                         ret = strdup(lua_tostring(lua_L, -1));
141                         lua_pop(lua_L, 1);
142                 }
143         }
144
145         return ret;
146 }
147
148 char *llua_getstring_read(const char *function, const char *arg)
149 {
150         char *func;
151         char *ret = NULL;
152
153         if(!lua_L) return NULL;
154
155         func = llua_do_read_call(function, arg, 1);
156         if (func) {
157                 if(!lua_isstring(lua_L, -1)) {
158                         ERR("llua_getstring_read: function %s didn't return a string, result discarded", func);
159                 } else {
160                         ret = strdup(lua_tostring(lua_L, -1));
161                         lua_pop(lua_L, 1);
162                 }
163         }
164
165         return ret;
166 }
167
168 int llua_getinteger(const char *args, int *per)
169 {
170         char *func;
171
172         if(!lua_L) return 0;
173
174         func = llua_do_call(args, 1);
175         if(func) {
176                 if(!lua_isnumber(lua_L, -1)) {
177                         ERR("llua_getinteger: function %s didn't return an integer, result discarded", func);
178                 } else {
179                         *per = lua_tointeger(lua_L, -1);
180                         lua_pop(lua_L, 1);
181                         return 1;
182                 }
183         }
184         return 0;
185 }
186
187 void llua_close(void)
188 {
189         llua_rm_notifies();
190         if(!lua_L) return;
191         lua_close(lua_L);
192         lua_L = NULL;
193 }
194
195 #ifdef HAVE_SYS_INOTIFY_H
196 struct _lua_notify_s {
197         int wd;
198         char name[DEFAULT_TEXT_BUFFER_SIZE];
199         struct _lua_notify_s *next;
200 };
201 static struct _lua_notify_s *lua_notifies = 0;
202
203 static struct _lua_notify_s *llua_notify_list_do_alloc(const char *name)
204 {
205         struct _lua_notify_s *ret = malloc(sizeof(struct _lua_notify_s));
206         memset(ret, 0, sizeof(struct _lua_notify_s));
207         strncpy(ret->name, name, DEFAULT_TEXT_BUFFER_SIZE);
208         return ret;
209 }
210
211 void llua_append_notify(const char *name)
212 {
213         /* do it */
214         struct _lua_notify_s *new_tail = 0;
215         if (!lua_notifies) {
216                 /* empty, fresh new digs */
217                 new_tail = lua_notifies = llua_notify_list_do_alloc(name);
218         } else {
219                 struct _lua_notify_s *tail = lua_notifies;
220                 while (tail->next) {
221                         tail = tail->next;
222                 }
223                 // should be @ the end now
224                 new_tail = llua_notify_list_do_alloc(name);
225                 tail->next = new_tail;
226         }
227         new_tail->wd = inotify_add_watch(inotify_fd,
228                         new_tail->name,
229                         IN_MODIFY);
230 }
231
232 void llua_rm_notifies(void)
233 {
234         /* git 'er done */
235         struct _lua_notify_s *head = lua_notifies;
236         struct _lua_notify_s *next = head->next;
237         if (!lua_notifies) return;
238         inotify_rm_watch(inotify_fd, head->wd);
239         free(head);
240         while (next) {
241                 head = next;
242                 next = head->next;
243                 inotify_rm_watch(inotify_fd, head->wd);
244                 free(head);
245         }
246         lua_notifies = 0;
247 }
248
249 void llua_inotify_query(int wd, int mask)
250 {
251         struct _lua_notify_s *head = lua_notifies;
252         if (mask & IN_MODIFY || mask & IN_IGNORED) {
253                 /* for whatever reason, i keep getting IN_IGNORED when the file is
254                  * modified */
255                 while (head) {
256                         if (head->wd == wd) {
257                                 llua_block_notify = 1;
258                                 llua_load(head->name);
259                                 llua_block_notify = 0;
260                                 if (mask & IN_IGNORED) {
261                                         /* for some reason we get IN_IGNORED here
262                                          * sometimes, so we need to re-add the watch */
263                                         head->wd = inotify_add_watch(inotify_fd,
264                                                         head->name,
265                                                         IN_MODIFY);
266                                 }
267                                 return;
268                         }
269                         head = head->next;
270                 }
271         }
272 }
273 #endif /* HAVE_SYS_INOTIFY_H */
274