adjust for changes to the parsing api.
[dana/openbox.git] / plugins / menu / fifo_menu.c
1 /*
2  * $Header$
3  *
4  * FFIO menu plugin
5  * Provides a menu from a FIFO located in ~/.openbox/fifo_menu/id
6  * Example:
7  * rc3:
8  *   <menu id="fonk" label="fonk" plugin="fifo_menu"></menu>
9  * Menu format
10  * <fifo_menu>
11  *    <item label="GLOVE.png">
12  *       <action name="execute">
13  *          <execute>
14  *             bsetbg "/home/woodblock/.openbox/backgrounds/GLOVE.png"
15  *          </execute>
16  *       </action>
17  *    </item>
18  *  </fifo_menu>
19  *
20  * If the attribute pid="true" is in the <menu>
21  */
22
23 #include <glib.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <stdio.h>
31
32 #include "kernel/menu.h"
33 #include "kernel/event.h"
34
35 static char *PLUGIN_NAME = "fifo_menu";
36
37 typedef struct Fifo_Menu_Data {
38     char *fifo;
39     char *buf; /* buffer to hold partially read menu */
40     unsigned long buflen; /* how many bytes are in the buffer */
41     int fd; /* file descriptor to read menu from */
42     gboolean use_pid;
43     event_fd_handler *handler;
44 } Fifo_Menu_Data;
45
46 #define FIFO_MENU(m) ((ObMenu *)m)
47 #define FIFO_MENU_DATA(m) ((Fifo_Menu_Data *)((ObMenu *)m)->plugin_data)
48
49
50 void fifo_menu_clean_up(ObMenu *m) {
51     if (FIFO_MENU_DATA(m)->buf != NULL) {
52         g_free(FIFO_MENU_DATA(m)->buf);
53         FIFO_MENU_DATA(m)->buf = NULL;
54         FIFO_MENU_DATA(m)->buflen = 0;
55     }
56
57     if (FIFO_MENU_DATA(m)->fd != -1) {
58         close(FIFO_MENU_DATA(m)->fd);
59         FIFO_MENU_DATA(m)->fd = -1;
60     }
61 }
62
63 void plugin_setup_config() { }
64 void plugin_startup()
65 { }
66 void plugin_shutdown() { }
67
68 void fifo_menu_handler(int fd, void *d) {
69     ObMenu *menu = d;
70     char *tmpbuf = NULL;
71     unsigned long num_read;
72 #ifdef DEBUG
73     /* because gdb is dumb */
74 #if 0
75     Fifo_Menu_Data *d = FIFO_MENU_DATA(menu);
76 #endif
77 #endif
78     
79     /* if the menu is shown this will go into busy loop :(
80      fix me*/
81     if (!menu->shown) { 
82         unsigned long num_realloc;
83         /* if we have less than a quarter BUFSIZ left, allocate more */
84         num_realloc = (BUFSIZ - (FIFO_MENU_DATA(menu)->buflen % BUFSIZ) <
85                        BUFSIZ >> 2) ?
86             0 : BUFSIZ;
87     
88         tmpbuf = g_try_realloc(FIFO_MENU_DATA(menu)->buf,             
89                                FIFO_MENU_DATA(menu)->buflen + num_realloc);
90
91         if (tmpbuf == NULL) {
92             g_warning("Unable to allocate memory for read()");
93             return;
94         }
95     
96         FIFO_MENU_DATA(menu)->buf = tmpbuf;
97     
98         num_read = read(fd,
99                         FIFO_MENU_DATA(menu)->buf +
100                         FIFO_MENU_DATA(menu)->buflen,
101                         num_realloc);
102
103         if (num_read == 0) { /* eof */
104             ObParseInst *i;
105             xmlDocPtr doc;
106             xmlNodePtr node;
107
108             menu->invalid = TRUE;
109             menu_clear(menu);
110
111             FIFO_MENU_DATA(menu)->buf[FIFO_MENU_DATA(menu)->buflen] = '\0';
112
113             i = parse_startup();
114
115             if (parse_load_mem(FIFO_MENU_DATA(menu)->buf,
116                                FIFO_MENU_DATA(menu)->buflen,
117                                "fifo_menu", &doc, &node))
118                 parse_menu_full(i, doc, node, menu, FALSE);
119
120             parse_shutdown(i);
121
122             fifo_menu_clean_up(menu);
123             
124             event_remove_fd(FIFO_MENU_DATA(menu)->handler->fd);
125         
126             if ((FIFO_MENU_DATA(menu)->fd =
127                  open(FIFO_MENU_DATA(menu)->fifo,
128                       O_NONBLOCK | O_RDONLY)) == -1) {
129                 g_warning("Can't reopen FIFO");
130                 fifo_menu_clean_up(menu);
131                 return;
132             }
133
134             FIFO_MENU_DATA(menu)->handler->fd = FIFO_MENU_DATA(menu)->fd;
135         
136             event_add_fd_handler(FIFO_MENU_DATA(menu)->handler);
137         } else if (num_read > 0) {
138             FIFO_MENU_DATA(menu)->buflen += num_read;
139             FIFO_MENU_DATA(menu)->buf[FIFO_MENU_DATA(menu)->buflen] = '\0';
140         }
141     }
142 }
143
144 void plugin_destroy (ObMenu *m)
145 {
146     fifo_menu_clean_up(m);
147     if (FIFO_MENU_DATA(m)->handler != NULL) {
148         g_free(FIFO_MENU_DATA(m)->handler);
149         FIFO_MENU_DATA(m)->handler = NULL;
150     }
151
152     if (FIFO_MENU_DATA(m)->fifo != NULL) {
153         g_free(FIFO_MENU_DATA(m)->fifo);
154         FIFO_MENU_DATA(m)->fifo = NULL;
155     }
156
157     if (FIFO_MENU_DATA(m)->buf != NULL) {
158         g_free(FIFO_MENU_DATA(m)->buf);
159         FIFO_MENU_DATA(m)->buf = NULL;
160     }
161  
162     g_free(m->plugin_data);
163
164     menu_free(m->name);
165 }
166
167 void *plugin_create(PluginMenuCreateData *data)
168 {
169     char *fifo;
170     char *dir;
171     event_fd_handler *h;
172     Fifo_Menu_Data *d;
173     ObMenu *m;
174     char *label = NULL, *id = NULL;
175     char *attr_pid = NULL;
176         
177     d = g_new(Fifo_Menu_Data, 1);
178
179     parse_attr_string("id", data->node, &id);
180     parse_attr_string("label", data->node, &label);
181     
182     if (parse_attr_string("pid", data->node, &attr_pid) &&
183         g_strcasecmp(attr_pid, "true") == 0) {
184         d->use_pid = TRUE;
185     } else
186         d->use_pid = FALSE;
187     
188     m = menu_new( (label != NULL ? label : ""),
189                   (id != NULL ? id : PLUGIN_NAME),
190                   data->parent);
191
192     if (data->parent)
193         menu_add_entry(data->parent, menu_entry_new_submenu(
194                            (label != NULL ? label : ""),
195                            m));
196
197     g_free(label);
198     g_free(id);
199     d->fd = -1;
200     d->buf = NULL;
201     d->buflen = 0;
202     d->handler = NULL;
203     
204     m->plugin = PLUGIN_NAME;
205
206     d->fd = -1;
207     
208     m->plugin_data = (void *)d;
209
210         
211     dir = g_build_filename(g_get_home_dir(), ".openbox",
212                           PLUGIN_NAME, NULL);
213
214     if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) == -1 && errno != EEXIST) {
215 /* technically, if ~/.openbox/fifo_menu exists and isn't a directory
216    this will fail, but we don't care because mkfifo will fail and warn
217    anyway */
218         g_warning("Can't create %s: %s", dir, strerror(errno));
219         g_free(dir);
220         plugin_destroy(m);
221         return NULL;
222     }
223
224     if (d->use_pid)
225     {
226         char *pid = g_strdup_printf("%s.%d", m->name, getpid());
227         fifo = g_build_filename(g_get_home_dir(), ".openbox",
228                                 PLUGIN_NAME,
229                                 pid, NULL);
230         g_free(pid);
231     } else {
232         fifo = g_build_filename(g_get_home_dir(), ".openbox",
233                                 PLUGIN_NAME,
234                                 m->name, NULL);
235     }
236     
237     if (mkfifo(fifo, S_IRUSR | S_IWUSR |
238                S_IRGRP | S_IWGRP | /* let umask do its thing */
239                S_IROTH | S_IWOTH) == -1 && errno != EEXIST) {
240         g_warning("Can't create FIFO %s: %s", fifo, strerror(errno));
241         g_free(fifo);
242         g_free(d);
243         menu_free(m->name);
244         return NULL;
245     }
246
247 /* open in non-blocking mode so we don't wait for a process to open FIFO
248    for writing */
249     if ((d->fd = open(fifo, O_NONBLOCK | O_RDONLY)) == -1) { 
250         g_warning("Can't open FIFO %s: %s", fifo, strerror(errno));
251         g_free(fifo);
252         g_free(d);
253         menu_free(m->name);
254         return NULL;
255     }
256
257     d->fifo = fifo;
258     
259     h = g_new(event_fd_handler, 1);
260
261     if (h == NULL) {
262         g_warning("Out of memory");
263         close(d->fd);
264         g_free(fifo);
265         g_free(d);
266         menu_free(m->name);
267         return NULL;
268     }
269     
270     h->fd = d->fd;
271     h->data = m;
272     h->handler = fifo_menu_handler;
273     d->handler = h;
274     
275     event_add_fd_handler(h);
276     
277     g_free(dir);
278     return (void *)m;
279 }